From 0dc137a0df71d6294e020d11f4fe5b88ac180d36 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Fri, 30 Nov 2012 09:07:39 -0600 Subject: [PATCH] Refactors Lab CS; uses different conversion for D50 and D65; adds getRgbItem --- src/colorspace.js | 132 ++++++++++++++++++++++------------- test/pdfs/issue1998.pdf.link | 1 + test/test_manifest.json | 8 +++ 3 files changed, 93 insertions(+), 48 deletions(-) create mode 100644 test/pdfs/issue1998.pdf.link diff --git a/src/colorspace.js b/src/colorspace.js index 2ee793434..f30c50578 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -25,19 +25,26 @@ var ColorSpace = (function ColorSpaceClosure() { ColorSpace.prototype = { /** - * Converts src array items representing color components values starting - * from srcOffset to RGB color. Returns the array of the rgb components - * items, each value ranging from [0,255]. + * Converts the color value to the RGB color. The color components are + * located in the src array starting from the srcOffset. Returns the array + * of the rgb components, each value ranging from [0,255]. */ getRgb: function ColorSpace_getRgb(src, srcOffset) { error('Should not call ColorSpace.getRgb'); }, /** - * Converts src array items representing color components values starting - * from srcOffset to RGB colors. The src is Uint8Array with the items that - * represent color components values, each value scaled to [0, 2^bits). - * Only count amount of values with be converted and placed into dest array - * starting from destOffset offset, each value scaled to [0,255]. + * Converts the color value to the RGB color, similar to the getRgb method. + * The result placed into the dest array starting from the destOffset. + */ + getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) { + error('Should not call ColorSpace.getRgbItem'); + }, + /** + * Converts the specified number of the color values to the RGB colors. + * The colors are located in the src array starting from the srcOffset. + * The result is placed into the dest array starting from the destOffset. + * The src array items shall be in [0,2^bits) range, the dest array items + * will be in [0,255] range. */ getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { @@ -57,9 +64,8 @@ var ColorSpace = (function ColorSpaceClosure() { return false; }, /** - * Converts src array items representing color components values starting - * from srcOffset to RGB colors similar to the getRgbBuffer. Returns - * the created buffer. + * Creates the output buffer and converts the specified number of the color + * values to the RGB colors, similar to the getRgbBuffer. */ createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset, count, bits) { @@ -263,12 +269,18 @@ var AlternateCS = (function AlternateCSClosure() { AlternateCS.prototype = { getRgb: function AlternateCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, + dest, destOffset) { var baseNumComps = this.base.numComps; var input = 'subarray' in src ? src.subarray(srcOffset, srcOffset + this.numComps) : Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps); var tinted = this.tintFn(input); - return this.base.getRgb(tinted, 0); + this.base.getRgbItem(tinted, 0, dest, destOffset); }, getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { @@ -276,7 +288,8 @@ var AlternateCS = (function AlternateCSClosure() { var base = this.base; var scale = 1 / ((1 << bits) - 1); var baseNumComps = base.numComps; - var isPassthrough = base.isPassthrough(8); + var isGetRgbBufferSupported = 'getRgbBuffer' in base; + var isPassthrough = base.isPassthrough(8) || !isGetRgbBufferSupported; var pos = isPassthrough ? destOffset : 0; var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count); var numComps = this.numComps; @@ -287,8 +300,13 @@ var AlternateCS = (function AlternateCSClosure() { scaled[j] = src[srcOffset++] * scale; } var tinted = tintFn(scaled); - for (var j = 0; j < baseNumComps; j++) { - baseBuf[pos++] = tinted[j] * 255; + if (isGetRgbBufferSupported) { + for (var j = 0; j < baseNumComps; j++) { + baseBuf[pos++] = tinted[j] * 255; + } + } else { + base.getRgbItem(tinted, 0, baseBuf, pos); + pos += baseNumComps; } } if (!isPassthrough) { @@ -353,6 +371,12 @@ var IndexedCS = (function IndexedCSClosure() { var start = src[srcOffset] * numComps; return this.base.getRgb(this.lookup, start); }, + getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, + dest, destOffset) { + var numComps = this.base.numComps; + var start = src[srcOffset] * numComps; + this.base.getRgbItem(this.lookup, start, dest, destOffset); + }, getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, dest, destOffset) { var base = this.base; @@ -388,9 +412,15 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() { DeviceGrayCS.prototype = { getRgb: function DeviceGrayCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, + dest, destOffset) { var c = (src[srcOffset] * 255) | 0; c = c < 0 ? 0 : c > 255 ? 255 : c; - return new Uint8Array([c, c, c]); + dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; }, getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { @@ -423,14 +453,18 @@ var DeviceRgbCS = (function DeviceRgbCSClosure() { } DeviceRgbCS.prototype = { getRgb: function DeviceRgbCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, + dest, destOffset) { var r = src[srcOffset] * 255; var g = src[srcOffset + 1] * 255; var b = src[srcOffset + 2] * 255; - var rgb = new Uint8Array(3); - rgb[0] = r < 0 ? 0 : r > 255 ? 255 : r; - rgb[1] = g < 0 ? 0 : g > 255 ? 255 : g; - rgb[2] = b < 0 ? 0 : b > 255 ? 255 : b; - return rgb; + dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r; + dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g; + dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b; }, getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { @@ -1183,6 +1217,10 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { convertToRgb(src, srcOffset, 1, rgb, 0); return rgb; }, + getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, + dest, destOffset) { + convertToRgb(src, srcOffset, 1, dest, destOffset); + }, getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var scale = 1 / ((1 << bits) - 1); @@ -1253,7 +1291,7 @@ var LabCS = (function LabCSClosure() { }; // Function g(x) from spec - function g(x) { + function fn_g(x) { if (x >= 6 / 29) return x * x * x; else @@ -1267,31 +1305,37 @@ var LabCS = (function LabCSClosure() { var bs = src[srcOffset + 2]; // Adjust limits of 'as' and 'bs' - as = as > cs.amax ? cs.amax : as; - as = as < cs.amin ? cs.amin : as; - bs = bs > cs.bmax ? cs.bmax : bs; - bs = bs < cs.bmin ? cs.bmin : bs; + as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as; + bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs; // Computes intermediate variables X,Y,Z as per spec var M = (Ls + 16) / 116; var L = M + (as / 500); var N = M - (bs / 200); - var X = cs.XW * g(L); - var Y = cs.YW * g(M); - var Z = cs.ZW * g(N); - // XYZ to RGB 3x3 matrix, from: - // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC18 - var XYZtoRGB = [3.240479, -1.537150, -0.498535, - -0.969256, 1.875992, 0.041556, - 0.055648, -0.204043, 1.057311]; + var X = cs.XW * fn_g(L); + var Y = cs.YW * fn_g(M); + var Z = cs.ZW * fn_g(N); - var rgb = Util.apply3dTransform(XYZtoRGB, [X, Y, Z]); + var r, g, b; + // Using different conversions for D50 and D65 white points, + // per http://www.color.org/srgb.pdf + if (cs.ZW < 1) { + // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249) + r = X * 3.1339 + Y * -1.6170 + Z * -0.4906; + g = X * -0.9785 + Y * 1.9160 + Z * 0.0333; + b = X * 0.0720 + Y * -0.2290 + Z * 1.4057; + } else { + // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888) + r = X * 3.2406 + Y * -1.5372 + Z * -0.4986; + g = X * -0.9689 + Y * 1.8758 + Z * 0.0415; + b = X * 0.0557 + Y * -0.2040 + Z * 1.0570; + } // clamp color values to [0,255] range - dest[destOffset] = rgb[0] < 0 ? 0 : rgb[0] > 1 ? 255 : rgb[0] * 255; - dest[destOffset + 1] = rgb[1] < 0 ? 0 : rgb[1] > 1 ? 255 : rgb[1] * 255; - dest[destOffset + 2] = rgb[2] < 0 ? 0 : rgb[2] > 1 ? 255 : rgb[2] * 255; + dest[destOffset] = r < 0 ? 0 : r > 1 ? 255 : r * 255; + dest[destOffset + 1] = g < 0 ? 0 : g > 1 ? 255 : g * 255; + dest[destOffset + 2] = b < 0 ? 0 : b > 1 ? 255 : b * 255; } LabCS.prototype = { @@ -1300,21 +1344,13 @@ var LabCS = (function LabCSClosure() { convertToRgb(this, src, srcOffset, rgb, 0); return rgb; }, - getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset) { - for (var i = 0; i < count; ++i) { - convertToRgb(this, src, srcOffset, dest, destOffset); - srcOffset += 3; - destOffset += 3; - } - - return rgbBuf; + getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) { + convertToRgb(this, src, srcOffset, dest, destOffset); }, getOutputLength: function LabCS_getOutputLength(inputLength) { return inputLength; }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) { // From Table 90 in Adobe's: // "Document management - Portable document format", 1st ed, 2008 diff --git a/test/pdfs/issue1998.pdf.link b/test/pdfs/issue1998.pdf.link new file mode 100644 index 000000000..0a2ba6b07 --- /dev/null +++ b/test/pdfs/issue1998.pdf.link @@ -0,0 +1 @@ +http://www.uctc.net/access/30/Access%2030%20-%2002%20-%20Horse%20Power.pdf \ No newline at end of file diff --git a/test/test_manifest.json b/test/test_manifest.json index 52866e325..e47701a60 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -735,6 +735,14 @@ "rounds": 1, "type": "load" }, + { "id": "issue1998", + "file": "pdfs/issue1998.pdf", + "md5": "586e0213be2f461360ec26770b5a4e48", + "rounds": 1, + "pageLimit": 2, + "link": true, + "type": "load" + }, { "id": "issue1878", "file": "pdfs/issue1878.pdf", "md5": "b4fb0ce7c19368e7104dce3d0d34bcb3",