Skip to content

Commit 663922f

Browse files
committed
Add a new parameter to JpegImage.getData to indicate the source of the image data (issue 9513)
The purpose of this patch is to provide a better default behaviour when `JpegImage` is used to parse standalone JPEG images with CMYK colour spaces. Since the issue that the patch concerns is somewhat of a special-case, the implementation utilizes the already existing decode support in an attempt to minimize the impact w.r.t. code size. *Please note:* It's always possible for the user of `JpegImage` to control image inversion, and thus override the new behaviour, by simply passing a custom `decodeTransform` array upon initialization.
1 parent 47bf12c commit 663922f

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

examples/image_decoders/jpeg_viewer.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ var jpegImage = new pdfjsImageDecoders.JpegImage();
5858
jpegImage.parse(typedArrayImage);
5959

6060
var width = jpegImage.width, height = jpegImage.height;
61-
var jpegData = jpegImage.getData(width, height, /* forceRGB = */ true);
61+
var jpegData = jpegImage.getData({
62+
width,
63+
height,
64+
forceRGB: true,
65+
});
6266

6367
// Render the JPEG image on a <canvas>.
6468
//

src/core/jpeg_stream.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,12 @@ let JpegStream = (function JpegStreamClosure() {
9797
const jpegImage = new JpegImage(jpegOptions);
9898

9999
jpegImage.parse(this.bytes);
100-
let data = jpegImage.getData(this.drawWidth, this.drawHeight,
101-
this.forceRGB);
100+
let data = jpegImage.getData({
101+
width: this.drawWidth,
102+
height: this.drawHeight,
103+
forceRGB: this.forceRGB,
104+
isSourcePDF: true,
105+
});
102106
this.buffer = data;
103107
this.bufferLength = data.length;
104108
this.eof = true;

src/core/jpg.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ var JpegImage = (function JpegImageClosure() {
975975
this.numComponents = this.components.length;
976976
},
977977

978-
_getLinearizedBlockData: function getLinearizedBlockData(width, height) {
978+
_getLinearizedBlockData(width, height, isSourcePDF = false) {
979979
var scaleX = this.width / width, scaleY = this.height / height;
980980

981981
var component, componentScaleX, componentScaleY, blocksPerScanline;
@@ -1013,7 +1013,24 @@ var JpegImage = (function JpegImageClosure() {
10131013
}
10141014

10151015
// decodeTransform contains pairs of multiplier (-256..256) and additive
1016-
const transform = this._decodeTransform;
1016+
let transform = this._decodeTransform;
1017+
1018+
// In PDF files, JPEG images with CMYK colour spaces are usually inverted
1019+
// (this can be observed by extracting the raw image data).
1020+
// Since the conversion algorithms (see below) were written primarily for
1021+
// the PDF use-cases, attempting to use `JpegImage` to parse standalone
1022+
// JPEG (CMYK) images may thus result in inverted images (see issue 9513).
1023+
//
1024+
// Unfortunately it's not (always) possible to tell, from the image data
1025+
// alone, if it needs to be inverted. Thus in an attempt to provide better
1026+
// out-of-box behaviour when `JpegImage` is used standalone, default to
1027+
// inverting JPEG (CMYK) images if and only if the image data does *not*
1028+
// come from a PDF file and no `decodeTransform` was passed by the user.
1029+
if (!transform && numComponents === 4 && !isSourcePDF) {
1030+
transform = new Int32Array([
1031+
-256, 255, -256, 255, -256, 255, -256, 255]);
1032+
}
1033+
10171034
if (transform) {
10181035
for (i = 0; i < dataLength;) {
10191036
for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
@@ -1162,14 +1179,14 @@ var JpegImage = (function JpegImageClosure() {
11621179
return data.subarray(0, offset);
11631180
},
11641181

1165-
getData: function getData(width, height, forceRGBoutput) {
1182+
getData({ width, height, forceRGB = false, isSourcePDF = false, }) {
11661183
if (this.numComponents > 4) {
11671184
throw new JpegError('Unsupported color mode');
11681185
}
1169-
// type of data: Uint8Array(width * height * numComponents)
1170-
var data = this._getLinearizedBlockData(width, height);
1186+
// Type of data: Uint8ClampedArray(width * height * numComponents)
1187+
var data = this._getLinearizedBlockData(width, height, isSourcePDF);
11711188

1172-
if (this.numComponents === 1 && forceRGBoutput) {
1189+
if (this.numComponents === 1 && forceRGB) {
11731190
var dataLength = data.length;
11741191
var rgbData = new Uint8ClampedArray(dataLength * 3);
11751192
var offset = 0;
@@ -1184,11 +1201,11 @@ var JpegImage = (function JpegImageClosure() {
11841201
return this._convertYccToRgb(data);
11851202
} else if (this.numComponents === 4) {
11861203
if (this._isColorConversionNeeded) {
1187-
if (forceRGBoutput) {
1204+
if (forceRGB) {
11881205
return this._convertYcckToRgb(data);
11891206
}
11901207
return this._convertYcckToCmyk(data);
1191-
} else if (forceRGBoutput) {
1208+
} else if (forceRGB) {
11921209
return this._convertCmykToRgb(data);
11931210
}
11941211
}

0 commit comments

Comments
 (0)