UNPKG

pngjs-image

Version:

JavaScript-based PNG image encoder, decoder, and manipulator

302 lines (241 loc) 5.65 kB
// Copyright 2015 Yahoo! Inc. // Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms. var BufferedStream = require('../utils/bufferedStream'); /** * @class Scaler * @module PNG * @submodule PNGCore * @param {Chunk} headerChunk Header chunk of data stream * @param {object} [options] Options for the compressor * @constructor */ var Scaler = function (headerChunk, options) { this._headerChunk = headerChunk; this._options = options || {}; }; /** * Gets the options * * @method getOptions * @return {object} */ Scaler.prototype.getOptions = function () { return this._options; }; /** * Scaler factor for 1-to-8-bit value conversion * * @static * @property SCALE_FACTOR_1_TO_8_BIT * @type {number} */ Scaler.SCALE_FACTOR_1_TO_8_BIT = 255; /** * Scaler factor for 8-to-1-bit value conversion * * @static * @property SCALE_FACTOR_8_TO_1_BIT * @type {number} */ Scaler.SCALE_FACTOR_8_TO_1_BIT = 1 / 255; /** * Scaler factor for 2-to-8-bit value conversion * * @static * @property SCALE_FACTOR_2_TO_8_BIT * @type {number} */ Scaler.SCALE_FACTOR_2_TO_8_BIT = 255 / 3; /** * Scaler factor for 8-to-2-bit value conversion * * @static * @property SCALE_FACTOR_8_TO_2_BIT * @type {number} */ Scaler.SCALE_FACTOR_8_TO_2_BIT = 3 / 255; /** * Scaler factor for 4-to-8-bit value conversion * * @static * @property SCALE_FACTOR_4_TO_8_BIT * @type {number} */ Scaler.SCALE_FACTOR_4_TO_8_BIT = 255 / 15; /** * Scaler factor for 8-to-4-bit value conversion * * @static * @property SCALE_FACTOR_8_TO_4_BIT * @type {number} */ Scaler.SCALE_FACTOR_8_TO_4_BIT = 15 / 255; /** * Scaler factor for 8-to-8-bit value conversion - identity * * @static * @property SCALE_FACTOR_8_TO_8_BIT * @type {number} */ Scaler.SCALE_FACTOR_8_TO_8_BIT = 1; /** * Scaler factor for 16-to-8-bit value conversion * * @static * @property SCALE_FACTOR_16_TO_8_BIT * @type {number} */ Scaler.SCALE_FACTOR_16_TO_8_BIT = 255 / 65535; /** * Scaler factor for 8-to-16-bit value conversion * * @static * @property SCALE_FACTOR_8_TO_16_BIT * @type {number} */ Scaler.SCALE_FACTOR_8_TO_16_BIT = 65535 / 255; /** * Gets the header chunk * * @method getHeaderChunk * @return {Chunk} */ Scaler.prototype.getHeaderChunk = function () { return this._headerChunk; }; /** * Encodes color values of an image * * @method encode * @param {Buffer} image * @return {Buffer} */ Scaler.prototype.encode = function (image) { return image; //TODO: Writing is currently only 8-bit }; /** * Determines the scaler according to header data * * @method _determineScaler * @return {object} * @private */ Scaler.prototype._determineScaler = function () { var headerChunk = this.getHeaderChunk(), isIndexed, bitDepth, result = null; isIndexed = headerChunk.isColorTypeIndexedColor(); bitDepth = headerChunk.getBitDepth(); switch (bitDepth) { case 1: result = isIndexed ? 1 : Scaler.SCALE_FACTOR_1_TO_8_BIT; break; case 2: result = isIndexed ? 1 : Scaler.SCALE_FACTOR_2_TO_8_BIT; break; case 4: result = isIndexed ? 1 : Scaler.SCALE_FACTOR_4_TO_8_BIT; break; case 8: result = isIndexed ? 1 : Scaler.SCALE_FACTOR_8_TO_8_BIT; break; case 16: result = Scaler.SCALE_FACTOR_16_TO_8_BIT; break; } return result; }; /** * Decodes color values * * @method decode * @param {Buffer} image * @return {Buffer} */ Scaler.prototype.decode = function (image) { var headerChunk = this.getHeaderChunk(), imageStream, outputStream, scaleFactor, offset = 0, is16Bit, samplesPerPixel = 4, len = image.length, // Since the image is here 16-bit i; imageStream = new BufferedStream(image, false); outputStream = new BufferedStream(null, null, headerChunk.getImageSizeInBytes()); is16Bit = (this.getHeaderChunk().getBitDepth() == 16); scaleFactor = this._determineScaler(); while (len > offset) { for(i = 0; i < samplesPerPixel; i++) { if (is16Bit) { outputStream.writeUInt8(Math.floor((imageStream.readUInt16BE() * scaleFactor) + 0.5)); } else { outputStream.writeUInt8(imageStream.readUInt16BE() * scaleFactor); } } offset += samplesPerPixel * 2; } return outputStream.toBuffer(true); }; /** * Scales a value according to the header * * @method decodeValue * @param {number} value * @return {number} */ Scaler.prototype.decodeValue = function (value) { var bitDepth = this.getHeaderChunk().getBitDepth(), result = null; switch(bitDepth) { case 1: result = value * Scaler.SCALE_FACTOR_1_TO_8_BIT; break; case 2: result = value * Scaler.SCALE_FACTOR_2_TO_8_BIT; break; case 4: result = value * Scaler.SCALE_FACTOR_4_TO_8_BIT; break; case 8: result = value * Scaler.SCALE_FACTOR_8_TO_8_BIT; break; case 16: result = value * Scaler.SCALE_FACTOR_16_TO_8_BIT; break; } result = Math.floor(result); return result; }; /** * Scales a value according to the header * * @method encodeValue * @param {number} value * @return {number} */ Scaler.prototype.encodeValue = function (value) { var bitDepth = this.getHeaderChunk().getBitDepth(), result = null; switch(bitDepth) { case 1: result = value * Scaler.SCALE_FACTOR_8_TO_1_BIT; break; case 2: result = value * Scaler.SCALE_FACTOR_8_TO_2_BIT; break; case 4: result = value * Scaler.SCALE_FACTOR_8_TO_4_BIT; break; case 8: result = value * Scaler.SCALE_FACTOR_8_TO_8_BIT; break; case 16: result = value * Scaler.SCALE_FACTOR_8_TO_16_BIT; break; } result = Math.ceil(result); return result; }; module.exports = Scaler;