diff --git a/README.md b/README.md index 3b2c51f..ceb7b0c 100644 --- a/README.md +++ b/README.md @@ -14,32 +14,28 @@ Example var fs = require('fs'), PNG = require('pngjs').PNG; -var png = new PNG({ - filterType: 4 -}); +fs.createReadStream('in.png') + .pipe(new PNG({ + filterType: 4 + })) + .on('parsed', function() { -png.on('parsed', function() { + for (var y = 0; y < this.height; y++) { + for (var x = 0; x < this.width; x++) { + var idx = (this.width * y + x) << 2; - for (var y = 0; y < png.height; y++) { - for (var x = 0; x < png.width; x++) { - var idx = (png.width * y + x) << 2; + // invert color + this.data[idx] = 255 - this.data[idx]; + this.data[idx+1] = 255 - this.data[idx+1]; + this.data[idx+2] = 255 - this.data[idx+2]; - // invert color - png.data[idx] = 255 - png.data[idx]; - png.data[idx+1] = 255 - png.data[idx+1]; - png.data[idx+2] = 255 - png.data[idx+2]; - - // and reduce opacity - png.data[idx+3] = png.data[idx+3] >> 1; + // and reduce opacity + this.data[idx+3] = this.data[idx+3] >> 1; + } } - } - png.pipe(fs.createWriteStream('out.png')); - - png.pack(); -}); - -fs.createReadStream('in.png').pipe(png); + this.pack().pipe(fs.createWriteStream('out.png')); + }); ``` For more examples see `examples` folder. @@ -54,40 +50,79 @@ As input any color type is accepted (grayscale, rgb, palette, grayscale with alp ## Class: PNG -`PNG` is readable and writeable `Stream`. +`PNG` is readable and writable `Stream`. + ### Options -- `width` - `int` -- `height` - `int` -- `checkCRC` - `boolean` default: `true` -- `deflateChunkSize` - `int` default: 32 kB -- `deflateLevel` - `int` default: 9 -- `filterType` - `int` default: -1 (auto) +- `width` - use this with `height` if you want to create png from scratch +- `height` - as above +- `checkCRC` - whether parser should be strict about checksums in source stream (default: `true`) +- `deflateChunkSize` - chunk size used for deflating data chunks, this should be power of 2 and must not be less than 256 and more than 32*1024 (default: 32 kB) +- `deflateLevel` - compression level for delate (default: 9) +- `filterType` - png filtering method for scanlines (default: -1 => auto) + + +### Event "metadata" +`function(metadata) { }` +Image's header has been parsed, metadata contains this information: +- `width` image size in pixels +- `height` image size in pixels +- `palette` image is paletted +- `color` image is not grayscale +- `alpha` image contains alpha channel + ### Event: "parsed" `function(data) { }` +Input image has been completly parsed, `data` is complete and ready for modification. + + +### Event: "error" +`function(error) { }` -### png.pack() -Starts converting data to PNG file Stream ### png.parse(data, [callback]) -Parses PNG file data. Alternatively you can stream data to PNG. +Parses PNG file data. Alternatively you can stream data to instance of PNG. Optional `callback` is once called on `error` or `parsed`. The callback gets two arguments `(err, data)`. +Returns `this` for method chaining. + + +### png.pack() +Starts converting data to PNG file Stream. + +Returns `this` for method chaining. + + +### png.bitblt(dst, sx, sy, w, h, dx, dy) +Helper for image manipulation, copies rectangle of pixels from current image (`sx`, `sy`, `w`, `h`) to `dst` image (at `dx`, `dy`). + +Returns `this` for method chaining. + + ### Property: width +Width of image in pixels + ### Property: height +Height of image in pixels + ### Property: data Buffer of image pixel data. Every pixel consists 4 bytes: R, G, B, A (opacity). + ### Property: gamma +Gamma of image (0 if not specified) Changelog ============ +### 0.3.0-alpha - 23 Aug 2012 + - Processing data as Streams, not complete Buffers of data + ### 0.2.0-alpha - 21 Aug 2012 - Input added palette, grayscale, no alpha support - Better scanline filter selection diff --git a/examples/fromdocs.js b/examples/fromdocs.js new file mode 100644 index 0000000..e530e53 --- /dev/null +++ b/examples/fromdocs.js @@ -0,0 +1,26 @@ + +var fs = require('fs'), + PNG = require('pngjs').PNG; + +fs.createReadStream('in.png') + .pipe(new PNG({ + filterType: 4 + })) + .on('parsed', function() { + + for (var y = 0; y < this.height; y++) { + for (var x = 0; x < this.width; x++) { + var idx = (this.width * y + x) << 2; + + // invert color + this.data[idx] = 255 - this.data[idx]; + this.data[idx+1] = 255 - this.data[idx+1]; + this.data[idx+2] = 255 - this.data[idx+2]; + + // and reduce opacity + this.data[idx+3] = this.data[idx+3] >> 1; + } + } + + this.pack().pipe(fs.createWriteStream('out.png')); + }); diff --git a/lib/chunkstream.js b/lib/chunkstream.js index a84f753..f3e1352 100755 --- a/lib/chunkstream.js +++ b/lib/chunkstream.js @@ -82,15 +82,14 @@ ChunkStream.prototype.write = function(data, encoding) { ChunkStream.prototype.end = function(data, encoding) { - if (!this.writable) { - this.emit('error', new Error('Stream not writable')); - return false; - } - if (data) this.write(data, encoding); this.writable = false; + // already destroyed + if (!this._buffers) return; + + // enqueue or handle end if (this._buffers.length == 0) { this._end(); } else { diff --git a/lib/png.js b/lib/png.js index 1b0a2a6..5af85d5 100755 --- a/lib/png.js +++ b/lib/png.js @@ -44,7 +44,7 @@ var PNG = exports.PNG = function(options) { this._parser = new Parser(options || {}); this._parser.on('error', this.emit.bind(this, 'error')); - this._parser.on('close', this.emit.bind(this, 'close')); + 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) { @@ -55,6 +55,7 @@ var PNG = exports.PNG = function(options) { 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')); }; @@ -108,12 +109,21 @@ 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; diff --git a/package.json b/package.json index 1899925..f507fed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pngjs", - "version": "0.2.0-alpha", + "version": "0.3.0-alpha", "description": "Simple PNG encoder/decoder", "author": "Kuba Niegowski", "contributors": [],