195 lines
4.3 KiB
JavaScript
195 lines
4.3 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
let util = require("util");
|
||
|
let Stream = require("stream");
|
||
|
let Parser = require("./parser-async");
|
||
|
let Packer = require("./packer-async");
|
||
|
let PNGSync = require("./png-sync");
|
||
|
|
||
|
let PNG = (exports.PNG = function (options) {
|
||
|
Stream.call(this);
|
||
|
|
||
|
options = options || {}; // eslint-disable-line no-param-reassign
|
||
|
|
||
|
// coerce pixel dimensions to integers (also coerces undefined -> 0):
|
||
|
this.width = options.width | 0;
|
||
|
this.height = options.height | 0;
|
||
|
|
||
|
this.data =
|
||
|
this.width > 0 && this.height > 0
|
||
|
? Buffer.alloc(4 * this.width * this.height)
|
||
|
: null;
|
||
|
|
||
|
if (options.fill && this.data) {
|
||
|
this.data.fill(0);
|
||
|
}
|
||
|
|
||
|
this.gamma = 0;
|
||
|
this.readable = this.writable = true;
|
||
|
|
||
|
this._parser = new Parser(options);
|
||
|
|
||
|
this._parser.on("error", this.emit.bind(this, "error"));
|
||
|
this._parser.on("close", this._handleClose.bind(this));
|
||
|
this._parser.on("metadata", this._metadata.bind(this));
|
||
|
this._parser.on("gamma", this._gamma.bind(this));
|
||
|
this._parser.on(
|
||
|
"parsed",
|
||
|
function (data) {
|
||
|
this.data = data;
|
||
|
this.emit("parsed", data);
|
||
|
}.bind(this)
|
||
|
);
|
||
|
|
||
|
this._packer = new Packer(options);
|
||
|
this._packer.on("data", this.emit.bind(this, "data"));
|
||
|
this._packer.on("end", this.emit.bind(this, "end"));
|
||
|
this._parser.on("close", this._handleClose.bind(this));
|
||
|
this._packer.on("error", this.emit.bind(this, "error"));
|
||
|
});
|
||
|
util.inherits(PNG, Stream);
|
||
|
|
||
|
PNG.sync = PNGSync;
|
||
|
|
||
|
PNG.prototype.pack = function () {
|
||
|
if (!this.data || !this.data.length) {
|
||
|
this.emit("error", "No data provided");
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
process.nextTick(
|
||
|
function () {
|
||
|
this._packer.pack(this.data, this.width, this.height, this.gamma);
|
||
|
}.bind(this)
|
||
|
);
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
PNG.prototype.parse = function (data, callback) {
|
||
|
if (callback) {
|
||
|
let onParsed, onError;
|
||
|
|
||
|
onParsed = function (parsedData) {
|
||
|
this.removeListener("error", onError);
|
||
|
|
||
|
this.data = parsedData;
|
||
|
callback(null, this);
|
||
|
}.bind(this);
|
||
|
|
||
|
onError = function (err) {
|
||
|
this.removeListener("parsed", onParsed);
|
||
|
|
||
|
callback(err, null);
|
||
|
}.bind(this);
|
||
|
|
||
|
this.once("parsed", onParsed);
|
||
|
this.once("error", onError);
|
||
|
}
|
||
|
|
||
|
this.end(data);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
PNG.prototype.write = function (data) {
|
||
|
this._parser.write(data);
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
PNG.prototype.end = function (data) {
|
||
|
this._parser.end(data);
|
||
|
};
|
||
|
|
||
|
PNG.prototype._metadata = function (metadata) {
|
||
|
this.width = metadata.width;
|
||
|
this.height = metadata.height;
|
||
|
|
||
|
this.emit("metadata", metadata);
|
||
|
};
|
||
|
|
||
|
PNG.prototype._gamma = function (gamma) {
|
||
|
this.gamma = gamma;
|
||
|
};
|
||
|
|
||
|
PNG.prototype._handleClose = function () {
|
||
|
if (!this._parser.writable && !this._packer.readable) {
|
||
|
this.emit("close");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
|
||
|
// eslint-disable-line max-params
|
||
|
// coerce pixel dimensions to integers (also coerces undefined -> 0):
|
||
|
/* eslint-disable no-param-reassign */
|
||
|
srcX |= 0;
|
||
|
srcY |= 0;
|
||
|
width |= 0;
|
||
|
height |= 0;
|
||
|
deltaX |= 0;
|
||
|
deltaY |= 0;
|
||
|
/* eslint-enable no-param-reassign */
|
||
|
|
||
|
if (
|
||
|
srcX > src.width ||
|
||
|
srcY > src.height ||
|
||
|
srcX + width > src.width ||
|
||
|
srcY + height > src.height
|
||
|
) {
|
||
|
throw new Error("bitblt reading outside image");
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
deltaX > dst.width ||
|
||
|
deltaY > dst.height ||
|
||
|
deltaX + width > dst.width ||
|
||
|
deltaY + height > dst.height
|
||
|
) {
|
||
|
throw new Error("bitblt writing outside image");
|
||
|
}
|
||
|
|
||
|
for (let y = 0; y < height; y++) {
|
||
|
src.data.copy(
|
||
|
dst.data,
|
||
|
((deltaY + y) * dst.width + deltaX) << 2,
|
||
|
((srcY + y) * src.width + srcX) << 2,
|
||
|
((srcY + y) * src.width + srcX + width) << 2
|
||
|
);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
PNG.prototype.bitblt = function (
|
||
|
dst,
|
||
|
srcX,
|
||
|
srcY,
|
||
|
width,
|
||
|
height,
|
||
|
deltaX,
|
||
|
deltaY
|
||
|
) {
|
||
|
// eslint-disable-line max-params
|
||
|
|
||
|
PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
PNG.adjustGamma = function (src) {
|
||
|
if (src.gamma) {
|
||
|
for (let y = 0; y < src.height; y++) {
|
||
|
for (let x = 0; x < src.width; x++) {
|
||
|
let idx = (src.width * y + x) << 2;
|
||
|
|
||
|
for (let i = 0; i < 3; i++) {
|
||
|
let sample = src.data[idx + i] / 255;
|
||
|
sample = Math.pow(sample, 1 / 2.2 / src.gamma);
|
||
|
src.data[idx + i] = Math.round(sample * 255);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
src.gamma = 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
PNG.prototype.adjustGamma = function () {
|
||
|
PNG.adjustGamma(this);
|
||
|
};
|