// Copyright (c) 2012 Kuba Niegowski // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. 'use strict'; var util = require('util'), Stream = require('stream'), Parser = require('./parser-async'), Packer = require('./packer'), PNGSync = require('./png-sync'); var PNG = exports.PNG = function(options) { Stream.call(this); options = options || {}; this.width = options.width || 0; this.height = options.height || 0; this.data = this.width > 0 && this.height > 0 ? new Buffer(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() { process.nextTick(function() { this._packer.pack(this.data, this.width, this.height); }.bind(this)); return this; }; PNG.prototype.parse = function(data, callback) { if (callback) { var onParsed = null, onError = null; this.once('parsed', onParsed = function(data) { this.removeListener('error', onError); this.data = data; callback(null, this); }.bind(this)); this.once('error', onError = function(err) { this.removeListener('parsed', onParsed); callback(err, null); }.bind(this)); } 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, sx, sy, w, h, dx, dy) { if (sx > src.width || sy > src.height || sx + w > src.width || sy + h > src.height) throw new Error('bitblt reading outside image'); if (dx > dst.width || dy > dst.height || dx + w > dst.width || dy + h > dst.height) throw new Error('bitblt writing outside image'); for (var y = 0; y < h; y++) { src.data.copy(dst.data, ((dy + y) * dst.width + dx) << 2, ((sy + y) * src.width + sx) << 2, ((sy + y) * src.width + sx + w) << 2 ); } }; PNG.prototype.bitblt = function(dst, sx, sy, w, h, dx, dy) { PNG.bitblt(this, dst, sx, sy, w, h, dx, dy); return this; }; PNG.adjustGamma = function(src) { if (src.gamma) { for (var y = 0; y < src.height; y++) { for (var x = 0; x < src.width; x++) { var idx = (src.width * y + x) << 2; for (var i = 0; i < 3; i++) { var 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); };