Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pngjs

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pngjs - npm Package Compare versions

Comparing version 0.2.0-alpha to 0.3.0-alpha

examples/fromdocs.js

3

examples/test/bg.js

@@ -27,3 +27,2 @@ #!/usr/bin/env node

png.pipe(fs.createWriteStream('bg.png'));
png.pack();
png.pack().pipe(fs.createWriteStream(__dirname + '/bg.png'));

@@ -9,8 +9,8 @@

for (var i = 0; i < files.length; i++) {
files.forEach(function(file) {
if (!files[i].match(/\.png$/i))
continue;
if (!file.match(/\.png$/i))
return;
fs.createReadStream(__dirname + '/img/' + files[i])
fs.createReadStream(__dirname + '/img/' + file)
.pipe(new PNG())

@@ -33,5 +33,8 @@ .on('parsed', function() {

this.pack();
}).pipe(fs.createWriteStream(__dirname + '/out/' + files[i]));
}
this.pack()
.pipe(fs.createWriteStream(__dirname + '/out/' + file));
});
});
});

@@ -25,14 +25,18 @@ // Copyright (c) 2012 Kuba Niegowski

zlib = require('zlib'),
events = require('events');
ChunkStream = require('./chunkstream');
var Filter = module.exports = function(options) {
events.EventEmitter.call(this);
var Filter = module.exports = function(width, height, Bpp, data, options) {
ChunkStream.call(this);
this._width = width;
this._height = height;
this._Bpp = Bpp;
this._data = data;
this._options = options;
this._line = 0;
options.filterType = 'filterType' in options ? options.filterType : -1;
this._width = 0;
this._height = 0;
this._filters = {

@@ -46,87 +50,109 @@ 0: this._filterNone.bind(this),

this.read(this._width * Bpp + 1, this._reverseFilterLine.bind(this));
};
util.inherits(Filter, events.EventEmitter);
util.inherits(Filter, ChunkStream);
Filter.prototype.prepare = function(width, height, type) {
if (type != 0)
throw new Error('Unsupported filter method');
this.width = width;
this.height = height;
var pixelBppMap = {
1: { // L
0: 0,
1: 0,
2: 0,
3: 0xff,
},
2: { // LA
0: 0,
1: 0,
2: 0,
3: 1
},
3: { // RGB
0: 0,
1: 1,
2: 2,
3: 0xff
},
4: { // RGBA
0: 0,
1: 1,
2: 2,
3: 3
}
};
Filter.prototype.unfilter = function(rawData, Bpp) {
Filter.prototype._reverseFilterLine = function(rawData) {
var pxLineLength = this.width << 2,
rawLineLength = this.width * Bpp + 1,
pxData = new Buffer(pxLineLength * this.height);
var pxData = this._data,
pxLineLength = this._width << 2,
pxRowPos = this._line * pxLineLength,
filter = rawData[0];
for (var y = 0; y < this.height; y++) {
if (filter == 0) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
var rawRowPos = rawLineLength * y + 1,
pxRowPos = y * pxLineLength,
filter = rawData[rawRowPos - 1];
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i];
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] : 0xff;
}
}
} else if (filter == 1) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
if (filter == 0) {
for (var x = 0; x < this.width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = rawRowPos + x * Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0;
for (var i = 0; i < Bpp; i++)
pxData[pxPos + i] = rawData[rawPos + i];
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + left : 0xff;
}
}
} else if (filter == 1) {
for (var x = 0; x < this.width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = rawRowPos + x * Bpp;
} else if (filter == 2) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
for (var i = 0; i < Bpp; i++) {
var left = x > 0 ? pxData[pxPos + i - 4] : 0;
pxData[pxPos + i] = rawData[rawPos + i] + left;
}
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0;
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + up : 0xff;
}
} else if (filter == 2) {
for (var x = 0; x < this.width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = rawRowPos + x * Bpp;
}
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 < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
} else if (filter == 3) {
for (var x = 0; x < this.width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = rawRowPos + x * Bpp;
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0,
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0,
add = Math.floor((left + up) / 2);
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[pxPos + i] = rawData[rawPos + i]
+ Math.floor((left + up) / 2);
}
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + add : 0xff;
}
} else if (filter == 4) {
for (var x = 0; x < this.width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = rawRowPos + x * Bpp;
}
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;
} else if (filter == 4) {
for (var x = 0; x < this._width; x++) {
var pxPos = pxRowPos + (x << 2),
rawPos = 1 + x * this._Bpp;
pxData[pxPos + i] = rawData[rawPos + i]
+ PaethPredictor(left, up, upLeft)
}
for (var i = 0; i < 4; i++) {
var idx = pixelBppMap[this._Bpp][i],
left = x > 0 ? pxData[pxPos + i - 4] : 0,
up = this._line > 0 ? pxData[pxPos - pxLineLength + i] : 0,
upLeft = x > 0 && this._line > 0
? pxData[pxPos - pxLineLength + i - 4] : 0,
add = PaethPredictor(left, up, upLeft);
pxData[pxPos + i] = idx != 0xff ? rawData[rawPos + idx] + add : 0xff;
}

@@ -136,42 +162,21 @@ }

// 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);
this._line++;
pxData[pxPos + 1] = pxData[pxPos + 2] = pxData[pxPos];
pxData[pxPos + 3] = 0xff;
}
if (this._line < this._height)
this.read(this._width * this._Bpp + 1, this._reverseFilterLine.bind(this));
else
this.emit('complete', this._data, this._width, this._height);
};
} 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;
}
Filter.prototype.filter = function() {
} // else RGBA
}
var pxData = this._data,
rawData = new Buffer(((this._width << 2) + 1) * this._height);
return pxData;
};
for (var y = 0; y < this._height; y++) {
Filter.prototype.filter = function(pxData, width, height) {
var rawData = new Buffer(((width << 2) + 1) * height);
for (var y = 0; y < height; y++) {
// find best filter for this line (with lowest sum of values)

@@ -183,3 +188,3 @@ if (this._options.filterType == -1) {

for (var f in this._filters) {
var sum = this._filters[f](pxData, y, width, height, null);
var sum = this._filters[f](pxData, y, null);
if (sum < min) {

@@ -194,3 +199,3 @@ sel = f;

}
this._filters[sel](pxData, y, width, height, rawData);
this._filters[sel](pxData, y, rawData);
}

@@ -200,5 +205,5 @@ return rawData;

Filter.prototype._filterNone = function(pxData, y, width, height, rawData) {
Filter.prototype._filterNone = function(pxData, y, rawData) {
var pxRowLength = width << 2,
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,

@@ -219,5 +224,5 @@ sum = 0;

Filter.prototype._filterSub = function(pxData, y, width, height, rawData) {
Filter.prototype._filterSub = function(pxData, y, rawData) {
var pxRowLength = width << 2,
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,

@@ -240,5 +245,5 @@ sum = 0;

Filter.prototype._filterUp = function(pxData, y, width, height, rawData) {
Filter.prototype._filterUp = function(pxData, y, rawData) {
var pxRowLength = width << 2,
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,

@@ -261,5 +266,5 @@ sum = 0;

Filter.prototype._filterAvg = function(pxData, y, width, height, rawData) {
Filter.prototype._filterAvg = function(pxData, y, rawData) {
var pxRowLength = width << 2,
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,

@@ -283,5 +288,5 @@ sum = 0;

Filter.prototype._filterPaeth = function(pxData, y, width, height, rawData) {
Filter.prototype._filterPaeth = function(pxData, y, rawData) {
var pxRowLength = width << 2,
var pxRowLength = this._width << 2,
rawRowLength = pxRowLength + 1,

@@ -288,0 +293,0 @@ sum = 0;

@@ -25,23 +25,11 @@ // Copyright (c) 2012 Kuba Niegowski

var util = require('util'),
Stream = require('stream'),
Compress = require('./compress'),
zlib = require('zlib'),
CrcStream = require('./crc'),
ChunkStream = require('./chunkstream'),
constants = require('./constants'),
Filter = require('./filter');
var signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
var TYPE_IHDR = 0x49484452;
var TYPE_IEND = 0x49454e44;
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) {
Stream.call(this);
ChunkStream.call(this);

@@ -54,3 +42,7 @@ this._options = options;

// input flags
this._inflate = null;
this._filter = null;
this._crc = null;
// input flags/metadata
this._palette = [];

@@ -60,181 +52,122 @@ this._colorType = 0;

this._chunks = {};
this._chunks[TYPE_IHDR] = this._parseIHDR.bind(this);
this._chunks[TYPE_IEND] = this._parseIEND.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);
this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this);
this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this);
this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this);
this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this);
this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this);
this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this);
this._compress = new Compress(options);
this._filter = new Filter(options);
this.writable = true;
this._initCompress();
this.on('error', this._handleError.bind(this));
this._handleSignature();
};
util.inherits(Parser, Stream);
util.inherits(Parser, ChunkStream);
Parser.prototype._initCompress = function() {
this._compress.on('error', this.emit.bind(this, 'error'));
Parser.prototype._handleError = function() {
this._compress.on('deflated', this._packData.bind(this));
this._compress.on('inflated', this._unfilter.bind(this));
};
this.writable = false;
Parser.prototype._parse = function(data) {
this.destroy();
var idx = 0;
try {
// check PNG file signature
while (idx < signature.length) {
if (data[idx] != signature[idx]) {
throw new Error('Invalid file signature');
}
idx++;
}
//console.log('Signature is ok');
// iterate chunks
while (idx < data.length) {
idx = this._parseChunk(data, idx);
}
}
catch(err) {
this.emit('error', err);
}
if (this._inflate)
this._inflate.destroy();
};
Parser.prototype._pack = function(width, height, data) {
// Signature
this.emit('data', new Buffer(signature));
this.emit('data', this._packIHDR(width, height));
// filter pixel data
var data = this._filter.filter(data, width, height);
// compress it
this._compress.deflate(data);
Parser.prototype._handleSignature = function() {
this.read(constants.PNG_SIGNATURE.length,
this._parseSignature.bind(this)
);
};
Parser.prototype._packData = function(data) {
Parser.prototype._parseSignature = function(data) {
// console.log('deflate', data.length);
var signature = constants.PNG_SIGNATURE;
this.emit('data', this._packIDAT(data));
this.emit('data', this._packIEND());
this.emit('end');
};
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];
}
for (var i = 0; i < signature.length; i++) {
if (data[i] != signature[i]) {
this.emit('error', new Error('Invalid file signature'));
return;
}
} 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);
}
this.read(8, this._parseChunkBegin.bind(this));
};
Parser.prototype._parseChunkBegin = function(data) {
// chunk content length
var length = data.readUInt32BE(0);
Parser.prototype._parseChunk = function(data, idx) {
if (this._hasIEND)
throw new Error('Not expected chunk after IEND');
// chunk size (only content)
var length = data.readUInt32BE(idx);
idx += 4;
// chunk type
var type = data.readUInt32BE(idx),
ancillary = !!(data[idx] & 0x20), // or critical
priv = !!(data[idx+1] & 0x20), // or public
safeToCopy = !!(data[idx+3] & 0x20), // or unsafe
var type = data.readUInt32BE(4),
name = '';
for (var i = 0; i < 4; i++)
name += String.fromCharCode(data[idx+i]);
idx += 4;
for (var i = 4; i < 8; i++)
name += String.fromCharCode(data[i]);
// console.log('chunk ', name, length);
// calc CRC (of chunk type and content)
var calcCrc = crc32(data.slice(idx - 4, idx + length)),
content = data.slice(idx, idx + length);
idx += length;
// chunk flags
var ancillary = !!(data[4] & 0x20), // or critical
priv = !!(data[5] & 0x20), // or public
safeToCopy = !!(data[7] & 0x20); // or unsafe
// read CRC
var fileCrc = data.readInt32BE(idx);
idx += 4;
if (!this._hasIHDR && type != constants.TYPE_IHDR) {
this.emit('error', new Error('Expected IHDR on beggining'));
return;
}
// and check CRC
if (this._options.checkCRC && calcCrc != fileCrc)
throw new Error('Crc error');
this._crc = new CrcStream();
this._crc.write(new Buffer(name));
if (!this._hasIHDR && type != TYPE_IHDR)
throw new Error('Expected IHDR on beggining');
if (this._chunks[type]) {
this._chunks[type](content);
return this._chunks[type](length);
} else if (!ancillary)
throw new Error('Unsupported critical chunk type ' + name);
// else
// console.log('Ignoring chunk', name, type.toString(16));
} else if (!ancillary) {
this.emit('error', new Error('Unsupported critical chunk type ' + name));
return;
} else {
this.read(length + 4, this._skipChunk.bind(this));
}
};
return idx;
Parser.prototype._skipChunk = function(data) {
this.read(8, this._parseChunkBegin.bind(this));
};
Parser.prototype._packChunk = function(type, data) {
Parser.prototype._handleChunkEnd = function() {
this.read(4, this._parseChunkEnd.bind(this));
};
var len = (data ? data.length : 0),
buf = new Buffer(len + 12);
Parser.prototype._parseChunkEnd = function(data) {
buf.writeUInt32BE(len, 0);
buf.writeUInt32BE(type, 4);
var fileCrc = data.readInt32BE(0),
calcCrc = this._crc.crc32();
if (data) data.copy(buf, 8);
// check CRC
if (this._options.checkCRC && calcCrc != fileCrc) {
this.emit('error', new Error('Crc error'));
return;
}
buf.writeInt32BE(crc32(buf.slice(4, buf.length - 4)), buf.length - 4);
return buf;
if (this._hasIEND) {
this.destroySoon();
} else {
this.read(8, this._parseChunkBegin.bind(this));
}
};
Parser.prototype._handleIHDR = function(length) {
this.read(length, this._parseIHDR.bind(this));
};
Parser.prototype._parseIHDR = function(data) {
this._crc.write(data);
var width = data.readUInt32BE(0),
height = data.readUInt32BE(4),
depth = data[8],
colorType = data[9], // 1 palette, 2 color, 4 alpha
colorType = data[9], // bits: 1 palette, 2 color, 4 alpha
compr = data[10],

@@ -249,34 +182,55 @@ filter = data[11],

if (depth != 8)
throw new Error('Unsupported bit depth ' + depth);
if (interlace != 0)
throw new Error('Unsupported interlace method');
if (depth != 8) {
this.emit('error', new Error('Unsupported bit depth ' + depth));
return;
}
if (!(colorType in colorTypeToBppMap)) {
this.emit('error', new Error('Unsupported color type'));
return;
}
if (compr != 0) {
this.emit('error', new Error('Unsupported compression method'));
return;
}
if (filter != 0) {
this.emit('error', new Error('Unsupported filter method'));
return;
}
if (interlace != 0) {
this.emit('error', new Error('Unsupported interlace method'));
return;
}
this._colorType = colorType;
this._compress.prepareInflate(compr);
this._filter.prepare(width, height, filter);
this._data = new Buffer(width * height * 4);
this._filter = new Filter(
width, height,
colorTypeToBppMap[this._colorType],
this._data,
this._options
);
this._hasIHDR = true;
this.emit('metadata', width, height);
this.emit('metadata', {
width: width,
height: height,
palette: !!(colorType & constants.COLOR_PALETTE),
color: !!(colorType & constants.COLOR_COLOR),
alpha: !!(colorType & constants.COLOR_ALPHA),
data: this._data
});
this._handleChunkEnd();
};
Parser.prototype._packIHDR = function(width, height) {
var buf = new Buffer(13);
buf.writeUInt32BE(width, 0);
buf.writeUInt32BE(height, 4);
buf[8] = 8;
buf[9] = 6; // colorType
buf[10] = 0; // compression
buf[11] = 0; // filter
buf[12] = 0; // interlace
return this._packChunk(TYPE_IHDR, buf);
Parser.prototype._handlePLTE = function(length) {
this.read(length, this._parsePLTE.bind(this));
};
Parser.prototype._parsePLTE = function(data) {
this._crc.write(data);
Parser.prototype._parsePLTE = function(data) {
var entries = Math.floor(data.length / 3);

@@ -293,16 +247,26 @@ // console.log('Palette:', entries);

}
this._handleChunkEnd();
};
Parser.prototype._handleTRNS = function(length) {
this.read(length, this._parseTRNS.bind(this));
};
Parser.prototype._parseTRNS = function(data) {
this._crc.write(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++)
if (this._palette.length == 0) {
this.emit('error', new Error('Transparency chunk must be after palette'));
return;
}
if (data.length > this._palette.length) {
this.emit('error', new Error('More transparent colors than palette size'));
return;
}
for (var i = 0; i < this._palette.length; i++) {
this._palette[i][3] = i < data.length ? data.readUInt8(i) : 0xff;
}
}

@@ -312,63 +276,90 @@

// there might be one gray/color defined as transparent
this._handleChunkEnd();
};
Parser.prototype._handleGAMA = function(length) {
this.read(length, this._parseGAMA.bind(this));
};
Parser.prototype._parseGAMA = function(data) {
this._crc.write(data);
this.emit('gamma', data.readUInt32BE(0) / 100000);
this._handleChunkEnd();
};
Parser.prototype._parseIDAT = function(data) {
Parser.prototype._handleIDAT = function(length) {
this.read(-length, this._parseIDAT.bind(this, length));
};
Parser.prototype._parseIDAT = function(length, data) {
this._crc.write(data);
if (this._colorType == 3 && this._palette.length == 0)
throw new Error('Expected palette not found');
this._compress.writeInflate(data);
};
if (!this._inflate) {
this._inflate = zlib.createInflate();
Parser.prototype._packIDAT = function(data) {
return this._packChunk(TYPE_IDAT, data);
this._inflate.on('error', this.emit.bind(this, 'error'));
this._filter.on('complete', this._reverseFiltered.bind(this));
this._inflate.pipe(this._filter);
}
this._inflate.write(data);
length -= data.length;
if (length > 0)
this._handleIDAT(length);
else
this._handleChunkEnd();
};
Parser.prototype._handleIEND = function(length) {
this.read(length, this._parseIEND.bind(this));
};
Parser.prototype._parseIEND = function(data) {
this._crc.write(data);
// no more data to inflate
this._compress.endInflate();
this._inflate.end();
this._hasIEND = true;
this._handleChunkEnd();
};
Parser.prototype._packIEND = function() {
return this._packChunk(TYPE_IEND, null);
var colorTypeToBppMap = {
0: 1,
2: 3,
3: 1,
4: 2,
6: 4
};
Parser.prototype._reverseFiltered = function(data, width, height) {
if (this._colorType == 3) { // paletted
// use values from palette
var pxLineLength = width << 2;
for (var y = 0; y < height; y++) {
var pxRowPos = y * pxLineLength;
for (var x = 0; x < width; x++) {
var pxPos = pxRowPos + (x << 2),
color = this._palette[data[pxPos]];
// prepare crc table as in PNG Specification
var crcTable = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
} else {
c = c >>> 1;
for (var i = 0; i < 4; i++)
data[pxPos + i] = color[i];
}
}
}
crcTable[i] = c;
}
function crc32(buf) {
var crc = -1;
for (var i = 0; i < buf.length; i++) {
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
}
return crc ^ -1;
}
this.emit('parsed', data);
};

@@ -25,8 +25,12 @@ // Copyright (c) 2012 Kuba Niegowski

var util = require('util'),
Parser = require('./parser');
Stream = require('stream'),
Parser = require('./parser'),
Packer = require('./packer');
var PNG = exports.PNG = function(options) {
Parser.call(this, options = options || {});
Stream.call(this);
options = options || {};
this.width = options.width || 0;

@@ -39,19 +43,31 @@ this.height = options.height || 0;

this.gamma = 0;
this.readable = this.writable = true;
this._buffers = [];
this._buffLen = 0;
this.on('metadata', this._metadata.bind(this));
this.on('gamma', this._gamma.bind(this));
this._parser = new Parser(options || {});
this.on('parsed', function(data) {
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, Parser);
util.inherits(PNG, Stream);
PNG.prototype.pack = function() {
this._pack(this.width, this.height, this.data);
process.nextTick(function() {
this._packer.pack(this.data, this.width, this.height);
}.bind(this));
return this;

@@ -81,3 +97,3 @@ };

this._parse(data);
this.end(data);
return this;

@@ -87,4 +103,3 @@ };

PNG.prototype.write = function(data) {
this._buffers.push(data);
this._buffLen += data.length;
this._parser.write(data);
return true;

@@ -94,12 +109,12 @@ };

PNG.prototype.end = function(data) {
if (data) this.write(data);
this._parse(Buffer.concat(this._buffers, this._buffLen));
this._buffers = [];
this._buffLen = 0;
this._parser.end(data);
};
PNG.prototype._metadata = function(width, height) {
this.width = width;
this.height = height;
this.data = null;
PNG.prototype._metadata = function(metadata) {
this.width = metadata.width;
this.height = metadata.height;
this.data = metadata.data;
delete metadata.data;
this.emit('metadata', metadata);
};

@@ -111,2 +126,8 @@

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) {

@@ -113,0 +134,0 @@

{
"name": "pngjs",
"version": "0.2.0-alpha",
"version": "0.3.0-alpha",
"description": "Simple PNG encoder/decoder",

@@ -5,0 +5,0 @@ "author": "Kuba Niegowski",

@@ -17,28 +17,24 @@ About

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'));
});
```

@@ -58,20 +54,35 @@ For more examples see `examples` folder.

## 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.
### png.pack()
Starts converting data to PNG file Stream
### Event: "error"
`function(error) { }`
### 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.

@@ -81,10 +92,31 @@ Optional `callback` is once called on `error` or `parsed`. The callback gets

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)

@@ -94,2 +126,5 @@ Changelog

### 0.3.0-alpha - 23 Aug 2012
- Processing data as Streams, not complete Buffers of data
### 0.2.0-alpha - 21 Aug 2012

@@ -96,0 +131,0 @@ - Input added palette, grayscale, no alpha support

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc