Comparing version 0.1.0-alpha to 0.2.0-alpha
@@ -8,3 +8,3 @@ #!/usr/bin/env node | ||
var png = new PNG({ | ||
filterType: 4 | ||
filterType: -1 | ||
}), | ||
@@ -21,9 +21,6 @@ src = fs.createReadStream(process.argv[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]; | ||
if (Math.abs(png.data[idx] - png.data[idx+1]) <= 1 | ||
&& Math.abs(png.data[idx+1] - png.data[idx+2]) <= 1) | ||
png.data[idx] = png.data[idx+1] = png.data[idx+2]; | ||
// and reduce opacity | ||
png.data[idx+3] = png.data[idx+3] >> 1; | ||
} | ||
@@ -30,0 +27,0 @@ } |
@@ -58,6 +58,6 @@ // Copyright (c) 2012 Kuba Niegowski | ||
Filter.prototype.unfilter = function(rawData) { | ||
Filter.prototype.unfilter = function(rawData, Bpp) { | ||
var pxLineLength = this.width << 2, | ||
rawLineLength = pxLineLength + 1, | ||
rawLineLength = this.width * Bpp + 1, | ||
pxData = new Buffer(pxLineLength * this.height); | ||
@@ -69,44 +69,98 @@ | ||
pxRowPos = y * pxLineLength, | ||
pxUpRowPos = pxRowPos - pxLineLength, | ||
filter = rawData[rawRowPos - 1]; | ||
if (filter == 0) { | ||
rawData.copy(pxData, pxRowPos, rawRowPos, rawRowPos + pxLineLength); | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
rawPos = rawRowPos + x * Bpp; | ||
for (var i = 0; i < Bpp; i++) | ||
pxData[pxPos + i] = rawData[rawPos + i]; | ||
} | ||
} else if (filter == 1) { | ||
for (var x = 0; x < pxLineLength; x++) { | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
rawPos = rawRowPos + x * Bpp; | ||
var left = x >= 4 ? pxData[pxRowPos + x - 4] : 0; | ||
pxData[pxRowPos + x] = rawData[rawRowPos + x] + left; | ||
for (var i = 0; i < Bpp; i++) { | ||
var left = x > 0 ? pxData[pxPos + i - 4] : 0; | ||
pxData[pxPos + i] = rawData[rawPos + i] + left; | ||
} | ||
} | ||
} else if (filter == 2) { | ||
for (var x = 0; x < pxLineLength; x++) { | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
rawPos = rawRowPos + x * Bpp; | ||
var up = y > 0 ? pxData[pxUpRowPos + x] : 0; | ||
pxData[pxRowPos + x] = rawData[rawRowPos + x] + up; | ||
for (var i = 0; i < Bpp; i++) { | ||
var up = y > 0 ? pxData[pxPos - pxLineLength + i] : 0; | ||
pxData[pxPos + i] = rawData[rawPos + i] + up; | ||
} | ||
} | ||
} else if (filter == 3) { | ||
for (var x = 0; x < pxLineLength; x++) { | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
rawPos = rawRowPos + x * Bpp; | ||
var left = x >= 4 ? pxData[pxRowPos + x - 4] : 0, | ||
up = y > 0 ? pxData[pxUpRowPos + x] : 0; | ||
for (var i = 0; i < Bpp; i++) { | ||
var left = x > 0 ? pxData[pxPos + i - 4] : 0, | ||
up = y > 0 ? pxData[pxPos - pxLineLength + i] : 0; | ||
pxData[pxRowPos + x] = rawData[rawRowPos + x] | ||
+ Math.floor((left + up) / 2); | ||
pxData[pxPos + i] = rawData[rawPos + i] | ||
+ Math.floor((left + up) / 2); | ||
} | ||
} | ||
} else if (filter == 4) { | ||
for (var x = 0; x < pxLineLength; x++) { | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
rawPos = rawRowPos + x * Bpp; | ||
var left = x >= 4 ? pxData[pxRowPos + x - 4] : 0, | ||
up = y > 0 ? pxData[pxUpRowPos + x] : 0, | ||
upLeft = x >= 4 && y > 0 ? pxData[pxUpRowPos + x - 4] : 0; | ||
for (var i = 0; i < Bpp; i++) { | ||
var left = x > 0 ? pxData[pxPos + i - 4] : 0, | ||
up = y > 0 ? pxData[pxPos - pxLineLength + i] : 0, | ||
upLeft = x > 0 && y > 0 | ||
? pxData[pxPos - pxLineLength + i - 4] : 0; | ||
pxData[pxRowPos + x] = rawData[rawRowPos + x] | ||
+ PaethPredictor(left, up, upLeft) | ||
pxData[pxPos + i] = rawData[rawPos + i] | ||
+ PaethPredictor(left, up, upLeft) | ||
} | ||
} | ||
} | ||
} | ||
// expand data to 32 bit | ||
for (var y = 0; y < this.height; y++) { | ||
var pxRowPos = y * pxLineLength; | ||
if (Bpp == 1) { // L | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2); | ||
pxData[pxPos + 1] = pxData[pxPos + 2] = pxData[pxPos]; | ||
pxData[pxPos + 3] = 0xff; | ||
} | ||
} else if (Bpp == 2) { // LA | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2); | ||
pxData[pxPos + 3] = pxData[pxPos + 1]; | ||
pxData[pxPos + 1] = pxData[pxPos + 2] = pxData[pxPos]; | ||
} | ||
} else if (Bpp == 3) { // RGB | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2); | ||
pxData[pxPos + 3] = 0xff; | ||
} | ||
} // else RGBA | ||
} | ||
return pxData; | ||
@@ -151,3 +205,3 @@ }; | ||
for (var x = 0; x < pxRowLength; x++) | ||
sum += pxData[y * pxRowLength + x]; | ||
sum += Math.abs(pxData[y * pxRowLength + x]); | ||
@@ -176,3 +230,3 @@ } else { | ||
if (!rawData) sum += val; | ||
if (!rawData) sum += Math.abs(val); | ||
else rawData[y * rawRowLength + 1 + x] = val; | ||
@@ -197,3 +251,3 @@ } | ||
if (!rawData) sum += val; | ||
if (!rawData) sum += Math.abs(val); | ||
else rawData[y * rawRowLength + 1 + x] = val; | ||
@@ -219,3 +273,3 @@ } | ||
if (!rawData) sum += val; | ||
if (!rawData) sum += Math.abs(val); | ||
else rawData[y * rawRowLength + 1 + x] = val; | ||
@@ -242,3 +296,3 @@ } | ||
if (!rawData) sum += val; | ||
if (!rawData) sum += Math.abs(val); | ||
else rawData[y * rawRowLength + 1 + x] = val; | ||
@@ -245,0 +299,0 @@ } |
@@ -35,4 +35,11 @@ // Copyright (c) 2012 Kuba Niegowski | ||
var TYPE_IDAT = 0x49444154; | ||
var TYPE_PLTE = 0x504c5445; | ||
var TYPE_tRNS = 0x74524e53; | ||
var TYPE_gAMA = 0x67414d41; | ||
var COLOR_PALETTE = 1; | ||
var COLOR_COLOR = 2; | ||
var COLOR_ALPHA = 4; | ||
var Parser = module.exports = function(options) { | ||
@@ -47,2 +54,6 @@ Stream.call(this); | ||
// input flags | ||
this._palette = []; | ||
this._colorType = 0; | ||
this._chunks = {}; | ||
@@ -52,2 +63,5 @@ this._chunks[TYPE_IHDR] = this._parseIHDR.bind(this); | ||
this._chunks[TYPE_IDAT] = this._parseIDAT.bind(this); | ||
this._chunks[TYPE_PLTE] = this._parsePLTE.bind(this); | ||
this._chunks[TYPE_tRNS] = this._parseTRNS.bind(this); | ||
this._chunks[TYPE_gAMA] = this._parseGAMA.bind(this); | ||
@@ -66,5 +80,3 @@ this._compress = new Compress(options); | ||
this._compress.on('deflated', this._packData.bind(this)); | ||
this._compress.on('inflated', function(data) { | ||
this.emit('parsed', this._filter.unfilter(data)); | ||
}.bind(this)); | ||
this._compress.on('inflated', this._unfilter.bind(this)); | ||
}; | ||
@@ -118,2 +130,43 @@ | ||
Parser.prototype._unfilter = function(data) { | ||
// expand data to 32 bit depending on colorType | ||
if (this._colorType == 0) { // L | ||
data = this._filter.unfilter(data, 1); // 1 Bpp | ||
} else if (this._colorType == 2) { // RGB | ||
data = this._filter.unfilter(data, 3); // 3 Bpp | ||
} else if (this._colorType == 3) { // I | ||
data = this._filter.unfilter(data, 1); // 1 Bpp | ||
// use values fom palette | ||
var pxLineLength = this.width << 2; | ||
for (var y = 0; y < this.height; y++) { | ||
var pxRowPos = y * pxLineLength; | ||
for (var x = 0; x < this.width; x++) { | ||
var pxPos = pxRowPos + (x << 2), | ||
color = this._palette[data[pxPos]]; | ||
for (var i = 0; i < 4; i++) | ||
data[pxPos + i] = color[i]; | ||
} | ||
} | ||
} else if (this._colorType == 4) { // LA | ||
data = this._filter.unfilter(data, 2); // 2 Bpp | ||
} else if (this._colorType == 6) { // RGBA | ||
data = this._filter.unfilter(data, 4); // 4 Bpp | ||
} else throw new Error('Unsupported color type'); | ||
this.emit('parsed', data); | ||
}; | ||
Parser.prototype._parseChunk = function(data, idx) { | ||
@@ -162,2 +215,4 @@ | ||
throw new Error('Unsupported critical chunk type ' + name); | ||
// else | ||
// console.log('Ignoring chunk', name, type.toString(16)); | ||
@@ -198,8 +253,8 @@ return idx; | ||
if (depth != 8) | ||
throw new Error('Unsupported bit depth'); | ||
if (colorType != 6) | ||
throw new Error('Unsupported color type'); | ||
throw new Error('Unsupported bit depth ' + depth); | ||
if (interlace != 0) | ||
throw new Error('Unsupported interlace method'); | ||
this._colorType = colorType; | ||
this._compress.prepareInflate(compr); | ||
@@ -228,3 +283,44 @@ this._filter.prepare(width, height, filter); | ||
Parser.prototype._parsePLTE = function(data) { | ||
var entries = Math.floor(data.length / 3); | ||
// console.log('Palette:', entries); | ||
for (var i = 0; i < entries; i++) { | ||
this._palette.push([ | ||
data.readUInt8(i * 3), | ||
data.readUInt8(i * 3 + 1), | ||
data.readUInt8(i * 3 + 2 ), | ||
0xff | ||
]); | ||
} | ||
}; | ||
Parser.prototype._parseTRNS = function(data) { | ||
// palette | ||
if (this._colorType == 3) { | ||
if (this._palette.length == 0) | ||
throw new Error('Transparency chunk must be after palette'); | ||
if (data.length > this._palette.length) | ||
throw new Error('More transparent colors than palette size'); | ||
for (var i = 0; i < this._palette.length; i++) | ||
this._palette[i][3] = i < data.length ? data.readUInt8(i) : 0xff; | ||
} | ||
// for colorType 0 (grayscale) and 2 (rgb) | ||
// there might be one gray/color defined as transparent | ||
}; | ||
Parser.prototype._parseGAMA = function(data) { | ||
this.emit('gamma', data.readUInt32BE(0) / 100000); | ||
}; | ||
Parser.prototype._parseIDAT = function(data) { | ||
if (this._colorType == 3 && this._palette.length == 0) | ||
throw new Error('Expected palette not found'); | ||
this._compress.writeInflate(data); | ||
@@ -231,0 +327,0 @@ }; |
@@ -29,3 +29,3 @@ // Copyright (c) 2012 Kuba Niegowski | ||
var PNG = exports.PNG = function(options) { | ||
Parser.call(this, options); | ||
Parser.call(this, options = options || {}); | ||
@@ -38,3 +38,3 @@ this.width = options.width || 0; | ||
this.on('metadata', this._metadata.bind(this)); | ||
this.gamma = 0; | ||
@@ -45,2 +45,5 @@ this.readable = this.writable = true; | ||
this.on('metadata', this._metadata.bind(this)); | ||
this.on('gamma', this._gamma.bind(this)); | ||
this.on('parsed', function(data) { | ||
@@ -55,2 +58,3 @@ this.data = data; | ||
this._pack(this.width, this.height, this.data); | ||
return this; | ||
}; | ||
@@ -80,2 +84,3 @@ | ||
this._parse(data); | ||
return this; | ||
}; | ||
@@ -86,2 +91,3 @@ | ||
this._buffLen += data.length; | ||
return true; | ||
}; | ||
@@ -101,1 +107,27 @@ | ||
}; | ||
PNG.prototype._gamma = function(gamma) { | ||
this.gamma = gamma; | ||
}; | ||
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; | ||
}; |
{ | ||
"name": "pngjs", | ||
"version": "0.1.0-alpha", | ||
"version": "0.2.0-alpha", | ||
"description": "Simple PNG encoder/decoder", | ||
@@ -5,0 +5,0 @@ "author": "Kuba Niegowski", |
@@ -18,6 +18,4 @@ About | ||
var png = new PNG({ | ||
filterType: 4 | ||
}), | ||
src = fs.createReadStream(process.argv[2]), | ||
dst = fs.createWriteStream(process.argv[3]); | ||
filterType: 4 | ||
}); | ||
@@ -40,6 +38,8 @@ png.on('parsed', function() { | ||
png.pipe(fs.createWriteStream('out.png')); | ||
png.pack(); | ||
}); | ||
src.pipe(png).pipe(dst); | ||
fs.createReadStream('in.png').pipe(png); | ||
``` | ||
@@ -51,7 +51,10 @@ For more examples see `examples` folder. | ||
Currently only true color mode with 8-bit color depth (per color) with alpha | ||
is supported. PNG cannot parse and create images with palette of colors. | ||
Interlaced mode is not supported either. | ||
As input any color type is accepted (grayscale, rgb, palette, grayscale with alpha, rgb with alpha) but 8 bit per sample (channel) is the only supported bit depth. Interlaced mode is not supported. | ||
## PNG | ||
### Supported ancillary chunks | ||
- `gAMA` - gamma, | ||
- `tRNS` - transparency (but only for paletted image) | ||
## Class: PNG | ||
`PNG` is readable and writeable `Stream`. | ||
@@ -86,5 +89,11 @@ | ||
### Property: gamma | ||
Changelog | ||
============ | ||
### 0.2.0-alpha - 21 Aug 2012 | ||
- Input added palette, grayscale, no alpha support | ||
- Better scanline filter selection | ||
### 0.1.0-alpha - 19 Aug 2012 | ||
@@ -91,0 +100,0 @@ - First version |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54301
37
746
122
3
80
5