// 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'), Packer = require('./packer'); 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.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.data = metadata.data; delete metadata.data; 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.prototype.bitblt = function(dst, sx, sy, w, h, dx, dy) { var src = this; 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 ); } return this; };