You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
5.7 KiB
282 lines
5.7 KiB
// Copyright 2015 Yahoo! Inc.
|
|
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.
|
|
|
|
var Interlace = require('./interlace');
|
|
var BufferedStream = require('../utils/bufferedStream');
|
|
|
|
/**
|
|
* @class Scanline Parser
|
|
* @module PNG
|
|
* @submodule PNGCore
|
|
* @param {Chunk} headerChunk Header chunk of data stream
|
|
* @param {object} [options] Options for the compressor
|
|
* @constructor
|
|
*/
|
|
var Parser = function (headerChunk, options) {
|
|
this._headerChunk = headerChunk;
|
|
this._options = options || {};
|
|
};
|
|
|
|
/**
|
|
* Gets the options
|
|
*
|
|
* @method getOptions
|
|
* @return {object}
|
|
*/
|
|
Parser.prototype.getOptions = function () {
|
|
return this._options;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the header chunk
|
|
*
|
|
* @method getHeaderChunk
|
|
* @return {Chunk}
|
|
*/
|
|
Parser.prototype.getHeaderChunk = function () {
|
|
return this._headerChunk;
|
|
};
|
|
|
|
|
|
/**
|
|
* Encodes an image
|
|
*
|
|
* @method encoder
|
|
* @param {Buffer} image
|
|
* @return {Buffer}
|
|
*/
|
|
Parser.prototype.encoder = function (image) {
|
|
return image;
|
|
//TODO: Writing is currently only 8-bit
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines the scanline-parser factory according to header data
|
|
*
|
|
* @method _determineParserFactory
|
|
* @return {object}
|
|
* @private
|
|
*/
|
|
Parser.prototype._determineParserFactory = function () {
|
|
|
|
var headerChunk = this.getHeaderChunk(),
|
|
result = null;
|
|
|
|
switch (headerChunk.getBitDepth()) {
|
|
case 1:
|
|
result = '_parse1bit';
|
|
break;
|
|
|
|
case 2:
|
|
result = '_parse2bit';
|
|
break;
|
|
|
|
case 4:
|
|
result = '_parse4bit';
|
|
break;
|
|
|
|
case 8:
|
|
result = '_parse8bit';
|
|
break;
|
|
|
|
case 16:
|
|
result = '_parse16bit';
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Decodes an image
|
|
*
|
|
* @method decoder
|
|
* @param {Buffer} image
|
|
* @return {Buffer} 16-bit image
|
|
*/
|
|
Parser.prototype.decode = function (image) {
|
|
|
|
var headerChunk = this.getHeaderChunk(),
|
|
interlace = new Interlace(headerChunk),
|
|
|
|
imageStream, outputStream,
|
|
|
|
bitDepth = headerChunk.getBitDepth(),
|
|
internalImageSize = headerChunk.getImageSizeInBytes() * 2, // 16-bit
|
|
parser;
|
|
|
|
imageStream = new BufferedStream(image, false);
|
|
outputStream = new BufferedStream(null, null, internalImageSize);
|
|
|
|
parser = this._determineParserFactory();
|
|
|
|
interlace.processPasses(function (width, height, scanLineLength) {
|
|
|
|
var i,
|
|
paddingAt,
|
|
length,
|
|
parserFn;
|
|
|
|
length = scanLineLength * height;
|
|
|
|
paddingAt = headerChunk.scanLineWithWidthPaddingAt(width);
|
|
parserFn = this[parser](paddingAt);
|
|
|
|
if (bitDepth === 16) {
|
|
for(i = 0; i < length; i += 2) {
|
|
parserFn(imageStream.readUInt16BE(), outputStream);
|
|
}
|
|
} else {
|
|
for(i = 0; i < length; i += 1) {
|
|
parserFn(imageStream.readUInt8(), outputStream);
|
|
}
|
|
}
|
|
|
|
}.bind(this));
|
|
|
|
return outputStream.toBuffer();
|
|
};
|
|
|
|
/**
|
|
* Parses a 1-bit scanline stream
|
|
*
|
|
* @method _parse1bit
|
|
* @param {number} paddingAt Defines the position of padding within each scanline
|
|
* @return {function}
|
|
* @private
|
|
*/
|
|
Parser.prototype._parse1bit = function (paddingAt) {
|
|
|
|
var byteCounter = 0;
|
|
|
|
return function (value, output) {
|
|
|
|
output.writeUInt16BE((value >> 7) & 1); byteCounter++;
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 6) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 5) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 4) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 3) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 2) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 1) & 1);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE(value & 1);
|
|
byteCounter++;
|
|
}
|
|
|
|
// Make sure that padding is removed
|
|
if (paddingAt && byteCounter >= paddingAt) {
|
|
byteCounter = 0;
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Parses a 2-bit scanline stream
|
|
*
|
|
* @method _parse2bit
|
|
* @param {number} paddingAt Defines the position of padding within each scanline
|
|
* @return {function}
|
|
* @private
|
|
*/
|
|
Parser.prototype._parse2bit = function (paddingAt) {
|
|
|
|
var byteCounter = 0;
|
|
|
|
return function (value, output) {
|
|
|
|
output.writeUInt16BE((value >> 6) & 3); byteCounter++;
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 4) & 3);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE((value >> 2) & 3);
|
|
byteCounter++;
|
|
}
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE(value & 3);
|
|
byteCounter++;
|
|
}
|
|
|
|
// Make sure that padding is removed
|
|
if (paddingAt && byteCounter >= paddingAt) {
|
|
byteCounter = 0;
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Parses a 4-bit scanline stream
|
|
*
|
|
* @method _parse4bit
|
|
* @param {number} paddingAt Defines the position of padding within each scanline
|
|
* @return {function}
|
|
* @private
|
|
*/
|
|
Parser.prototype._parse4bit = function (paddingAt) {
|
|
|
|
var byteCounter = 0;
|
|
|
|
return function (value, output) {
|
|
|
|
output.writeUInt16BE((value >> 4) & 15); byteCounter++;
|
|
if (!paddingAt || byteCounter < paddingAt) {
|
|
output.writeUInt16BE(value & 15);
|
|
byteCounter++;
|
|
}
|
|
|
|
// Make sure that padding is removed
|
|
if (paddingAt && byteCounter >= paddingAt) {
|
|
byteCounter = 0;
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Parses a 8-bit scanline stream
|
|
*
|
|
* @method _parse8bit
|
|
* @return {function}
|
|
* @private
|
|
*/
|
|
Parser.prototype._parse8bit = function () {
|
|
return function (value, output) {
|
|
output.writeUInt16BE(value);
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Parses a 16-bit scanline stream
|
|
*
|
|
* @method _parse16bit
|
|
* @return {function}
|
|
* @private
|
|
*/
|
|
Parser.prototype._parse16bit = function () {
|
|
return function (value, output) {
|
|
output.writeUInt16BE(value);
|
|
};
|
|
};
|
|
|
|
module.exports = Parser;
|