diff --git a/src/colorspace.js b/src/colorspace.js index 231ff6923..87d0083c0 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -154,6 +154,29 @@ var ColorSpace = (function ColorSpaceClosure() { } return null; }; + /** + * Checks if a decode map matches the default decode map for a color space. + * This handles the general decode maps where there are two values per + * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color. + * This does not handle Lab, Indexed, or Pattern decode maps since they are + * slightly different. + * @param {Array} decode Decode map (usually from an image). + * @param {Number} n Number of components the color space has. + */ + ColorSpace.isDefaultDecode = function colorSpaceIsDefaultDecode(decode, n) { + if (!decode) + return true; + + if (n * 2 !== decode.length) { + warning('The decode map is not the correct length'); + return true; + } + for (var i = 0, ii = decode.length; i < ii; i += 2) { + if (decode[i] != 0 || decode[i + 1] != 1) + return false; + } + return true; + }; return ColorSpace; })(); @@ -200,6 +223,9 @@ var AlternateCS = (function AlternateCSClosure() { baseBuf[pos++] = 255 * tinted[j]; } return base.getRgbBuffer(baseBuf, 8); + }, + isDefaultDecode: function altcs_isDefaultDecode(decodeMap) { + ColorSpace.isDefaultDecode(decodeMap, this.numComps); } }; @@ -267,6 +293,10 @@ var IndexedCS = (function IndexedCSClosure() { } return base.getRgbBuffer(baseBuf, 8); + }, + isDefaultDecode: function indexcs_isDefaultDecode(decodeMap) { + // indexed color maps shouldn't be changed + return true; } }; return IndexedCS; @@ -295,6 +325,9 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() { rgbBuf[j++] = c; } return rgbBuf; + }, + isDefaultDecode: function graycs_isDefaultDecode(decodeMap) { + ColorSpace.isDefaultDecode(decodeMap, this.numComps); } }; return DeviceGrayCS; @@ -319,6 +352,10 @@ var DeviceRgbCS = (function DeviceRgbCSClosure() { for (i = 0; i < length; ++i) rgbBuf[i] = (scale * input[i]) | 0; return rgbBuf; + }, + isDefaultDecode: function rgbcs_isDefaultDecode(decodeMap) { + return 0 == decodeMap[0] == decodeMap[2] == decodeMap[4] && + 1 == decodeMap[1] == decodeMap[3] == decoeMap[5]; } }; return DeviceRgbCS; @@ -403,6 +440,9 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { } return rgbBuf; + }, + isDefaultDecode: function cmykcs_isDefaultDecode(decodeMap) { + ColorSpace.isDefaultDecode(decodeMap, this.numComps); } }; diff --git a/src/image.js b/src/image.js index f848b12ae..2f99dee54 100644 --- a/src/image.js +++ b/src/image.js @@ -25,11 +25,10 @@ var PDFImage = (function PDFImageClosure() { } } /** - * Decode and clamp a value. + * Decode and clamp a value. The formula is different from the spec because we + * don't decode to float range [0,1], we decode it in the [0,max] range. */ - function decode(value, addend, coefficient, max) { - // This formula is different from the spec because we don't decode to - // float range [0,1], we decode it in the [0,max] range. + function decodeAndClamp(value, addend, coefficient, max) { value = addend + value * coefficient; // Clamp the value to the range return value < 0 ? 0 : value > max ? max : value; @@ -79,7 +78,10 @@ var PDFImage = (function PDFImageClosure() { } this.decode = dict.get('Decode', 'D'); - if (this.decode && !this.hasDefaultDecode()) { + this.needsDecode = false; + if (this.decode && this.colorSpace && + !this.colorSpace.isDefaultDecode(this.decode)) { + this.needsDecode = true; // Do some preprocessing to avoid more math. var max = (1 << bitsPerComponent) - 1; this.decodeCoefficients = []; @@ -128,12 +130,13 @@ var PDFImage = (function PDFImageClosure() { PDFImage.prototype = { getComponents: function getComponents(buffer) { var bpc = this.bpc; - var defaultDecode = this.hasDefaultDecode(); + var needsDecode = this.needsDecode; var decodeMap = this.decode; - if (decodeMap) console.time('getComps'); - // This image doesn't require extra work - if (bpc == 8 && defaultDecode) + + // This image doesn't require any extra work. + if (bpc == 8 && !needsDecode) return buffer; + var bufferLength = buffer.length; var width = this.width; var height = this.height; @@ -145,7 +148,7 @@ var PDFImage = (function PDFImageClosure() { bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length); var rowComps = width * numComps; var decodeAddends, decodeCoefficients; - if (!defaultDecode) { + if (needsDecode) { decodeAddends = this.decodeAddends; decodeCoefficients = this.decodeCoefficients; } @@ -156,7 +159,7 @@ var PDFImage = (function PDFImageClosure() { for (var i = 0, ii = length; i < ii; ++i) { var compIndex = i % numComps; var value = buffer[i]; - value = decode(value, decodeAddends[compIndex], + value = decodeAndClamp(value, decodeAddends[compIndex], decodeCoefficients[compIndex], max); output[i] = value; } @@ -201,9 +204,9 @@ var PDFImage = (function PDFImageClosure() { var remainingBits = bits - bpc; var value = buf >> remainingBits; - if (!defaultDecode) { + if (needsDecode) { var compIndex = i % numComps; - value = decode(value, decodeAddends[compIndex], + value = decodeAndClamp(value, decodeAddends[compIndex], decodeCoefficients[compIndex], max); } output[i] = value; @@ -304,22 +307,6 @@ var PDFImage = (function PDFImageClosure() { getImageBytes: function getImageBytes(length) { this.image.reset(); return this.image.getBytes(length); - }, - hasDefaultDecode: function hasDefaultDecode() { - // TODO lab color as a way different decode map - if (!this.decode) - return true; - var numComps = this.numComps; - var decode = this.decode; - if (numComps * 2 !== decode.length) { - warning('The decode map is not the correct length'); - return true; - } - for (var i = 0, ii = decode.length; i < ii; i += 2) { - if (decode[i] != 0 || decode[i + 1] != 1) - return false; - } - return true; } }; return PDFImage;