3 var util = require('util');
4 var Stream = require('stream');
5 var Parser = require('./parser-async');
6 var Packer = require('./packer-async');
7 var PNGSync = require('./png-sync');
10 var PNG = exports.PNG = function(options) {
13 options = options || {}; // eslint-disable-line no-param-reassign
15 // coerce pixel dimensions to integers (also coerces undefined -> 0):
16 this.width = options.width | 0;
17 this.height = options.height | 0;
19 this.data = this.width > 0 && this.height > 0 ?
20 new Buffer(4 * this.width * this.height) : null;
22 if (options.fill && this.data) {
27 this.readable = this.writable = true;
29 this._parser = new Parser(options);
31 this._parser.on('error', this.emit.bind(this, 'error'));
32 this._parser.on('close', this._handleClose.bind(this));
33 this._parser.on('metadata', this._metadata.bind(this));
34 this._parser.on('gamma', this._gamma.bind(this));
35 this._parser.on('parsed', function(data) {
37 this.emit('parsed', data);
40 this._packer = new Packer(options);
41 this._packer.on('data', this.emit.bind(this, 'data'));
42 this._packer.on('end', this.emit.bind(this, 'end'));
43 this._parser.on('close', this._handleClose.bind(this));
44 this._packer.on('error', this.emit.bind(this, 'error'));
47 util.inherits(PNG, Stream);
51 PNG.prototype.pack = function() {
53 if (!this.data || !this.data.length) {
54 this.emit('error', 'No data provided');
58 process.nextTick(function() {
59 this._packer.pack(this.data, this.width, this.height, this.gamma);
66 PNG.prototype.parse = function(data, callback) {
69 var onParsed, onError;
71 onParsed = function(parsedData) {
72 this.removeListener('error', onError);
74 this.data = parsedData;
78 onError = function(err) {
79 this.removeListener('parsed', onParsed);
84 this.once('parsed', onParsed);
85 this.once('error', onError);
92 PNG.prototype.write = function(data) {
93 this._parser.write(data);
97 PNG.prototype.end = function(data) {
98 this._parser.end(data);
101 PNG.prototype._metadata = function(metadata) {
102 this.width = metadata.width;
103 this.height = metadata.height;
105 this.emit('metadata', metadata);
108 PNG.prototype._gamma = function(gamma) {
112 PNG.prototype._handleClose = function() {
113 if (!this._parser.writable && !this._packer.readable) {
119 PNG.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params
120 // coerce pixel dimensions to integers (also coerces undefined -> 0):
121 /* eslint-disable no-param-reassign */
128 /* eslint-enable no-param-reassign */
130 if (srcX > src.width || srcY > src.height || srcX + width > src.width || srcY + height > src.height) {
131 throw new Error('bitblt reading outside image');
134 if (deltaX > dst.width || deltaY > dst.height || deltaX + width > dst.width || deltaY + height > dst.height) {
135 throw new Error('bitblt writing outside image');
138 for (var y = 0; y < height; y++) {
139 src.data.copy(dst.data,
140 ((deltaY + y) * dst.width + deltaX) << 2,
141 ((srcY + y) * src.width + srcX) << 2,
142 ((srcY + y) * src.width + srcX + width) << 2
148 PNG.prototype.bitblt = function(dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params
150 PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
154 PNG.adjustGamma = function(src) {
156 for (var y = 0; y < src.height; y++) {
157 for (var x = 0; x < src.width; x++) {
158 var idx = (src.width * y + x) << 2;
160 for (var i = 0; i < 3; i++) {
161 var sample = src.data[idx + i] / 255;
162 sample = Math.pow(sample, 1 / 2.2 / src.gamma);
163 src.data[idx + i] = Math.round(sample * 255);
171 PNG.prototype.adjustGamma = function() {
172 PNG.adjustGamma(this);