Browse Source

Refactor jpg.js and include forceRGBoutput, correct style of image.js

This refactors getData to be more readable and extracts all the color
conversion algorithms to their own functions. The resulting code was then
cleaned up.
This also introduces a flag `forceRGBoutput` to getData, that allows to always
get the data as a `width * height * 3` bytes long RGB buffer
Thorben Bochenek 11 years ago
parent
commit
e7fe45a5c4
  1. 165
      external/jpgjs/jpg.js
  2. 15
      src/core/image.js

165
external/jpgjs/jpg.js vendored

@ -782,9 +782,10 @@ var JpegImage = (function jpegImage() {
blocksPerColumn: component.blocksPerColumn blocksPerColumn: component.blocksPerColumn
}); });
} }
this.numComponents = this.components.length;
}, },
getData: function getData(width, height) { _getLinearizedBlockData: function getLinearizedBlockData(width, height) {
var scaleX = this.width / width, scaleY = this.height / height; var scaleX = this.width / width, scaleY = this.height / height;
var component, componentScaleX, componentScaleY; var component, componentScaleX, componentScaleY;
@ -842,80 +843,124 @@ var JpegImage = (function jpegImage() {
} }
} }
} }
return data;
},
// ... then transform colors, if necessary _isColorConversionNeeded: function isColorConversionNeeded() {
switch (numComponents) { if (this.adobe && this.adobe.transformCode) {
case 1: case 2: break;
// no color conversion for one or two compoenents
case 3:
// The default transform for three components is true
colorTransform = true;
// The adobe transform marker overrides any previous setting // The adobe transform marker overrides any previous setting
if (this.adobe && this.adobe.transformCode) return true;
colorTransform = true; } else if (this.numComponents == 3) {
else if (typeof this.colorTransform !== 'undefined') return true;
colorTransform = !!this.colorTransform; } else {
return false;
}
},
if (colorTransform) { _convertYccToRgb: function convertYccToRgb(data) {
for (i = 0; i < dataLength; i += numComponents) { var Y, Cb, Cr;
for (var i = 0; i < data.length; i += this.numComponents) {
Y = data[i ]; Y = data[i ];
Cb = data[i + 1]; Cb = data[i + 1];
Cr = data[i + 2]; Cr = data[i + 2];
data[i ] = clamp0to255(Y + 1.402 * (Cr - 128));
R = clamp0to255(Y + 1.402 * (Cr - 128)); data[i + 1] = clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
G = clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); data[i + 2] = clamp0to255(Y + 1.772 * (Cb - 128));
B = clamp0to255(Y + 1.772 * (Cb - 128));
data[i ] = R;
data[i + 1] = G;
data[i + 2] = B;
}
} }
break; return data;
case 4: },
// The default transform for four components is false
colorTransform = false;
// The adobe transform marker overrides any previous setting
if (this.adobe && this.adobe.transformCode)
colorTransform = true;
else if (typeof this.colorTransform !== 'undefined')
colorTransform = !!this.colorTransform;
if (colorTransform) { _convertYcckToRgb: function convertYcckToRgb(data) {
for (i = 0; i < dataLength; i += numComponents) { var Y, Cb, Cr, K, C, M, Ye, oneMinusK255th;
var outputData = new Uint8Array((data.length / 4) * 3);
var offset = 0;
for (var i = 0; i < data.length; i += this.numComponents) {
Y = data[i]; Y = data[i];
Cb = data[i + 1]; Cb = data[i + 1];
Cr = data[i + 2]; Cr = data[i + 2];
K = data[i + 3];
C = 255 - clamp0to255(Y + 1.402 * (Cr - 128)); C = 255 - clamp0to255(Y + 1.402 * (Cr - 128));
M = 255 - clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); M = 255 - clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
Ye = 255 - clamp0to255(Y + 1.772 * (Cb - 128)); Ye = 255 - clamp0to255(Y + 1.772 * (Cb - 128));
oneMinusK255th = (1 - K / 255);
data[i ] = C; outputData[offset++] = 255 - clamp0to255(C * oneMinusK255th + K);
data[i + 1] = M; outputData[offset++] = 255 - clamp0to255(M * oneMinusK255th + K);
data[i + 2] = Ye; outputData[offset++] = 255 - clamp0to255(Ye * oneMinusK255th + K);
// K is unchanged
} }
return outputData;
},
_convertYcckToCmyk: function convertYcckToCmyk(data) {
var Y, Cb, Cr;
for (var i = 0; i < data.length; i += this.numComponents) {
Y = data[i];
Cb = data[i + 1];
Cr = data[i + 2];
data[i ] = 255 - clamp0to255(Y + 1.402 * (Cr - 128));
data[i + 1] = 255 - clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
data[i + 2] = 255 - clamp0to255(Y + 1.772 * (Cb - 128));
// K in data[i + 3] is unchanged
} }
break; return data;
default: },
_convertCmykToRgb: function convertCmykToRgb(data) {
var C, M, Y, K, oneMinusK255th;
var outputData = new Uint8Array((data.length / 4) * 3);
var offset = 0;
for (var i = 0; i < data.length; i += this.numComponents) {
C = data[i ];
M = data[i + 1];
Y = data[i + 2];
K = data[i + 3];
oneMinusK255th = (1 - K / 255);
outputData[offset++] = 255 - clamp0to255(C * oneMinusK255th + K);
outputData[offset++] = 255 - clamp0to255(M * oneMinusK255th + K);
outputData[offset++] = 255 - clamp0to255(Y * oneMinusK255th + K);
// K in data[i + 3] is unchanged
}
return outputData;
},
getData: function getData(width, height, forceRGBoutput) {
var i;
var Y, Cb, Cr, K, C, M, Ye, R, G, B;
var colorTransform;
if (this.numComponents > 4) {
throw 'Unsupported color mode'; throw 'Unsupported color mode';
} }
// type of data: Uint8Array(width * height * numComponents)
var data = this._getLinearizedBlockData(width, height);
if (this.numComponents === 3) {
return this._convertYccToRgb(data);
} else if (this.numComponents === 4) {
if (this._isColorConversionNeeded()) {
if (forceRGBoutput) {
return this._convertYcckToRgb(data);
} else {
return this._convertYcckToCmyk(data);
}
} else {
return this._convertCmykToRgb(data);
}
}
return data; return data;
}, },
copyToImageData: function copyToImageData(imageData) { copyToImageData: function copyToImageData(imageData) {
var width = imageData.width, height = imageData.height; var width = imageData.width;
var height = imageData.height;
var imageDataBytes = width * height * 4; var imageDataBytes = width * height * 4;
var imageDataArray = imageData.data; var imageDataArray = imageData.data;
var data = this.getData(width, height); var data = this.getData(width, height, /* forceRGBoutput = */true);
var i = 0, j = 0; var i = 0, j = 0;
var Y, K, C, M, R, G, B;
switch (this.components.length) { switch (this.components.length) {
case 1: case 1:
var Y;
while (j < imageDataBytes) { while (j < imageDataBytes) {
Y = data[i++]; Y = data[i++];
imageDataArray[j++] = Y; imageDataArray[j++] = Y;
imageDataArray[j++] = Y; imageDataArray[j++] = Y;
imageDataArray[j++] = Y; imageDataArray[j++] = Y;
@ -923,31 +968,11 @@ var JpegImage = (function jpegImage() {
} }
break; break;
case 3: case 3:
while (j < imageDataBytes) {
R = data[i++];
G = data[i++];
B = data[i++];
imageDataArray[j++] = R;
imageDataArray[j++] = G;
imageDataArray[j++] = B;
imageDataArray[j++] = 255;
}
break;
case 4: case 4:
while (j < imageDataBytes) { while (j < imageDataBytes) {
C = data[i++]; imageDataArray[j++] = data[i++];
M = data[i++]; imageDataArray[j++] = data[i++];
Y = data[i++]; imageDataArray[j++] = data[i++];
K = data[i++];
R = 255 - clamp0to255(C * (1 - K / 255) + K);
G = 255 - clamp0to255(M * (1 - K / 255) + K);
B = 255 - clamp0to255(Y * (1 - K / 255) + K);
imageDataArray[j++] = R;
imageDataArray[j++] = G;
imageDataArray[j++] = B;
imageDataArray[j++] = 255; imageDataArray[j++] = 255;
} }
break; break;

15
src/core/image.js

@ -44,6 +44,7 @@ var PDFImage = (function PDFImageClosure() {
return Promise.resolve(image); return Promise.resolve(image);
} }
} }
/** /**
* Decode and clamp a value. The formula is different from the spec because we * 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. * don't decode to float range [0,1], we decode it in the [0,max] range.
@ -53,6 +54,7 @@ var PDFImage = (function PDFImageClosure() {
// Clamp the value to the range // Clamp the value to the range
return (value < 0 ? 0 : (value > max ? max : value)); return (value < 0 ? 0 : (value > max ? max : value));
} }
function PDFImage(xref, res, image, inline, smask, mask, isMask) { function PDFImage(xref, res, image, inline, smask, mask, isMask) {
this.image = image; this.image = image;
var dict = image.dict; var dict = image.dict;
@ -279,11 +281,13 @@ var PDFImage = (function PDFImageClosure() {
this.smask && this.smask.width || 0, this.smask && this.smask.width || 0,
this.mask && this.mask.width || 0); this.mask && this.mask.width || 0);
}, },
get drawHeight() { get drawHeight() {
return Math.max(this.height, return Math.max(this.height,
this.smask && this.smask.height || 0, this.smask && this.smask.height || 0,
this.mask && this.mask.height || 0); this.mask && this.mask.height || 0);
}, },
decodeBuffer: function PDFImage_decodeBuffer(buffer) { decodeBuffer: function PDFImage_decodeBuffer(buffer) {
var bpc = this.bpc; var bpc = this.bpc;
var numComps = this.numComps; var numComps = this.numComps;
@ -309,6 +313,7 @@ var PDFImage = (function PDFImageClosure() {
} }
} }
}, },
getComponents: function PDFImage_getComponents(buffer) { getComponents: function PDFImage_getComponents(buffer) {
var bpc = this.bpc; var bpc = this.bpc;
@ -385,6 +390,7 @@ var PDFImage = (function PDFImageClosure() {
} }
return output; return output;
}, },
fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height, fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height,
actualHeight, image) { actualHeight, image) {
var smask = this.smask; var smask = this.smask;
@ -451,6 +457,7 @@ var PDFImage = (function PDFImageClosure() {
} }
} }
}, },
undoPreblend: function PDFImage_undoPreblend(buffer, width, height) { undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
var matte = this.smask && this.smask.matte; var matte = this.smask && this.smask.matte;
if (!matte) { if (!matte) {
@ -467,7 +474,7 @@ var PDFImage = (function PDFImageClosure() {
var alpha = buffer[i + 3]; var alpha = buffer[i + 3];
if (alpha === 0) { if (alpha === 0) {
// according formula we have to get Infinity in all components // according formula we have to get Infinity in all components
// making it as white (tipical paper color) should be okay // making it white (tipical paper color) should be okay
buffer[i] = 255; buffer[i] = 255;
buffer[i + 1] = 255; buffer[i + 1] = 255;
buffer[i + 2] = 255; buffer[i + 2] = 255;
@ -479,6 +486,7 @@ var PDFImage = (function PDFImageClosure() {
buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]); buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]);
} }
}, },
createImageData: function PDFImage_createImageData(forceRGBA) { createImageData: function PDFImage_createImageData(forceRGBA) {
var drawWidth = this.drawWidth; var drawWidth = this.drawWidth;
var drawHeight = this.drawHeight; var drawHeight = this.drawHeight;
@ -529,7 +537,6 @@ var PDFImage = (function PDFImageClosure() {
return imgData; return imgData;
} }
} }
// imgArray can be incomplete (e.g. after CCITT fax encoding). // imgArray can be incomplete (e.g. after CCITT fax encoding).
var actualHeight = 0 | (imgArray.length / rowBytes * var actualHeight = 0 | (imgArray.length / rowBytes *
drawHeight / originalHeight); drawHeight / originalHeight);
@ -567,6 +574,7 @@ var PDFImage = (function PDFImageClosure() {
return imgData; return imgData;
}, },
fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) { fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
var numComps = this.numComps; var numComps = this.numComps;
if (numComps != 1) { if (numComps != 1) {
@ -589,7 +597,7 @@ var PDFImage = (function PDFImageClosure() {
length = width * height; length = width * height;
if (this.needsDecode) { if (this.needsDecode) {
// invert and scale to {0, 255} // invert and scale to {0, 255}
for (i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
buffer[i] = (comps[i] - 1) & 255; buffer[i] = (comps[i] - 1) & 255;
} }
} else { } else {
@ -611,6 +619,7 @@ var PDFImage = (function PDFImageClosure() {
buffer[i] = (scale * comps[i]) | 0; buffer[i] = (scale * comps[i]) | 0;
} }
}, },
getImageBytes: function PDFImage_getImageBytes(length) { getImageBytes: function PDFImage_getImageBytes(length) {
this.image.reset(); this.image.reset();
return this.image.getBytes(length); return this.image.getBytes(length);

Loading…
Cancel
Save