Fix where image component decoding takes place.
This commit is contained in:
parent
f627281626
commit
93b99e7352
93
src/image.js
93
src/image.js
@ -49,7 +49,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) {
|
function PDFImage(xref, res, image, inline, smask, mask, isMask) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
if (image.getParams) {
|
if (image.getParams) {
|
||||||
// JPX/JPEG2000 streams directly contain bits per component
|
// JPX/JPEG2000 streams directly contain bits per component
|
||||||
@ -95,8 +95,9 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
|
|
||||||
this.decode = dict.get('Decode', 'D');
|
this.decode = dict.get('Decode', 'D');
|
||||||
this.needsDecode = false;
|
this.needsDecode = false;
|
||||||
if (this.decode && this.colorSpace &&
|
if (this.decode &&
|
||||||
!this.colorSpace.isDefaultDecode(this.decode)) {
|
((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) ||
|
||||||
|
(isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) {
|
||||||
this.needsDecode = true;
|
this.needsDecode = true;
|
||||||
// Do some preprocessing to avoid more math.
|
// Do some preprocessing to avoid more math.
|
||||||
var max = (1 << bitsPerComponent) - 1;
|
var max = (1 << bitsPerComponent) - 1;
|
||||||
@ -114,7 +115,7 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
this.smask = new PDFImage(xref, res, smask, false);
|
this.smask = new PDFImage(xref, res, smask, false);
|
||||||
} else if (mask) {
|
} else if (mask) {
|
||||||
if (isStream(mask)) {
|
if (isStream(mask)) {
|
||||||
this.mask = new PDFImage(xref, res, mask, false);
|
this.mask = new PDFImage(xref, res, mask, false, null, null, true);
|
||||||
} else {
|
} else {
|
||||||
// Color key mask (just an array).
|
// Color key mask (just an array).
|
||||||
this.mask = mask;
|
this.mask = mask;
|
||||||
@ -216,13 +217,37 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
return this.height;
|
return this.height;
|
||||||
return Math.max(this.height, this.smask.height);
|
return Math.max(this.height, this.smask.height);
|
||||||
},
|
},
|
||||||
|
decodeBuffer: function PDFImage_decodeBuffer(buffer) {
|
||||||
|
var bpc = this.bpc;
|
||||||
|
var decodeMap = this.decode;
|
||||||
|
var numComps = this.numComps;
|
||||||
|
|
||||||
|
var decodeAddends, decodeCoefficients;
|
||||||
|
var decodeAddends = this.decodeAddends;
|
||||||
|
var decodeCoefficients = this.decodeCoefficients;
|
||||||
|
var max = (1 << bpc) - 1;
|
||||||
|
|
||||||
|
if (bpc === 1) {
|
||||||
|
// If the buffer needed decode that means it just needs to be inverted.
|
||||||
|
for (var i = 0, ii = buffer.length; i < ii; i++) {
|
||||||
|
buffer[i] = +!(buffer[i]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = 0;
|
||||||
|
for (var i = 0, ii = this.width * this.height; i < ii; i++) {
|
||||||
|
for (var j = 0; j < numComps; j++) {
|
||||||
|
buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j],
|
||||||
|
decodeCoefficients[j], max);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
getComponents: function PDFImage_getComponents(buffer) {
|
getComponents: function PDFImage_getComponents(buffer) {
|
||||||
var bpc = this.bpc;
|
var bpc = this.bpc;
|
||||||
var needsDecode = this.needsDecode;
|
|
||||||
var decodeMap = this.decode;
|
|
||||||
|
|
||||||
// This image doesn't require any extra work.
|
// This image doesn't require any extra work.
|
||||||
if (bpc == 8 && !needsDecode)
|
if (bpc === 8)
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
var bufferLength = buffer.length;
|
var bufferLength = buffer.length;
|
||||||
@ -235,29 +260,11 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
var output = bpc <= 8 ? new Uint8Array(length) :
|
var output = bpc <= 8 ? new Uint8Array(length) :
|
||||||
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||||
var rowComps = width * numComps;
|
var rowComps = width * numComps;
|
||||||
var decodeAddends, decodeCoefficients;
|
|
||||||
if (needsDecode) {
|
|
||||||
decodeAddends = this.decodeAddends;
|
|
||||||
decodeCoefficients = this.decodeCoefficients;
|
|
||||||
}
|
|
||||||
var max = (1 << bpc) - 1;
|
var max = (1 << bpc) - 1;
|
||||||
|
|
||||||
if (bpc == 8) {
|
if (bpc === 1) {
|
||||||
// Optimization for reading 8 bpc images that have a decode.
|
|
||||||
for (var i = 0, ii = length; i < ii; ++i) {
|
|
||||||
var compIndex = i % numComps;
|
|
||||||
var value = buffer[i];
|
|
||||||
value = decodeAndClamp(value, decodeAddends[compIndex],
|
|
||||||
decodeCoefficients[compIndex], max);
|
|
||||||
output[i] = value;
|
|
||||||
}
|
|
||||||
} else if (bpc == 1) {
|
|
||||||
// Optimization for reading 1 bpc images.
|
// Optimization for reading 1 bpc images.
|
||||||
var valueZero = 0, valueOne = 1;
|
|
||||||
if (decodeMap) {
|
|
||||||
valueZero = decodeMap[0] ? 1 : 0;
|
|
||||||
valueOne = decodeMap[1] ? 1 : 0;
|
|
||||||
}
|
|
||||||
var mask = 0;
|
var mask = 0;
|
||||||
var buf = 0;
|
var buf = 0;
|
||||||
|
|
||||||
@ -274,7 +281,7 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
mask = 128;
|
mask = 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
output[i] = !(buf & mask) ? valueZero : valueOne;
|
output[i] = +!!(buf & mask);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The general case that handles all other bpc values.
|
// The general case that handles all other bpc values.
|
||||||
@ -292,12 +299,7 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
|
|
||||||
var remainingBits = bits - bpc;
|
var remainingBits = bits - bpc;
|
||||||
var value = buf >> remainingBits;
|
var value = buf >> remainingBits;
|
||||||
if (needsDecode) {
|
output[i] = value < 0 ? 0 : value > max ? max : value;
|
||||||
var compIndex = i % numComps;
|
|
||||||
value = decodeAndClamp(value, decodeAddends[compIndex],
|
|
||||||
decodeCoefficients[compIndex], max);
|
|
||||||
}
|
|
||||||
output[i] = value;
|
|
||||||
buf = buf & ((1 << remainingBits) - 1);
|
buf = buf & ((1 << remainingBits) - 1);
|
||||||
bits = remainingBits;
|
bits = remainingBits;
|
||||||
}
|
}
|
||||||
@ -397,21 +399,27 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
// 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 *
|
||||||
height / originalHeight);
|
height / originalHeight);
|
||||||
|
var comps = this.getComponents(imgArray);
|
||||||
|
// Build opacity here since color key masking needs to be perormed on
|
||||||
|
// undecoded values.
|
||||||
|
var opacity = this.getOpacity(width, height, comps);
|
||||||
|
|
||||||
var comps = this.colorSpace.createRgbBuffer(this.getComponents(imgArray),
|
if (this.needsDecode) {
|
||||||
0, originalWidth * originalHeight, bpc);
|
this.decodeBuffer(comps);
|
||||||
|
}
|
||||||
|
var rgbBuf = this.colorSpace.createRgbBuffer(comps, 0,
|
||||||
|
originalWidth * originalHeight, bpc);
|
||||||
if (originalWidth != width || originalHeight != height)
|
if (originalWidth != width || originalHeight != height)
|
||||||
comps = PDFImage.resize(comps, this.bpc, 3, originalWidth,
|
rgbBuf = PDFImage.resize(rgbBuf, this.bpc, 3, originalWidth,
|
||||||
originalHeight, width, height);
|
originalHeight, width, height);
|
||||||
var compsPos = 0;
|
var compsPos = 0;
|
||||||
var opacity = this.getOpacity(width, height, imgArray);
|
|
||||||
var opacityPos = 0;
|
var opacityPos = 0;
|
||||||
var length = width * actualHeight * 4;
|
var length = width * actualHeight * 4;
|
||||||
|
|
||||||
for (var i = 0; i < length; i += 4) {
|
for (var i = 0; i < length; i += 4) {
|
||||||
buffer[i] = comps[compsPos++];
|
buffer[i] = rgbBuf[compsPos++];
|
||||||
buffer[i + 1] = comps[compsPos++];
|
buffer[i + 1] = rgbBuf[compsPos++];
|
||||||
buffer[i + 2] = comps[compsPos++];
|
buffer[i + 2] = rgbBuf[compsPos++];
|
||||||
buffer[i + 3] = opacity[opacityPos++];
|
buffer[i + 3] = opacity[opacityPos++];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -429,6 +437,9 @@ var PDFImage = (function PDFImageClosure() {
|
|||||||
var imgArray = this.getImageBytes(height * rowBytes);
|
var imgArray = this.getImageBytes(height * rowBytes);
|
||||||
|
|
||||||
var comps = this.getComponents(imgArray);
|
var comps = this.getComponents(imgArray);
|
||||||
|
if (this.needsDecode) {
|
||||||
|
this.decodeBuffer(comps);
|
||||||
|
}
|
||||||
var length = width * height;
|
var length = width * height;
|
||||||
// we aren't using a colorspace so we need to scale the value
|
// we aren't using a colorspace so we need to scale the value
|
||||||
var scale = 255 / ((1 << bpc) - 1);
|
var scale = 255 / ((1 << bpc) - 1);
|
||||||
|
1
test/pdfs/issue2770.pdf.link
Normal file
1
test/pdfs/issue2770.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://bugzilla.mozilla.org/attachment.cgi?id=718826
|
@ -971,6 +971,14 @@
|
|||||||
"link": true,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue2770",
|
||||||
|
"file": "pdfs/issue2770.pdf",
|
||||||
|
"md5": "36070d756d06eaa35c2227efb069fb1b",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq",
|
||||||
|
"about": "Has a 4 bit per component image with mask and decode."
|
||||||
|
},
|
||||||
{ "id": "p020121130574743273239",
|
{ "id": "p020121130574743273239",
|
||||||
"file": "pdfs/P020121130574743273239.pdf",
|
"file": "pdfs/P020121130574743273239.pdf",
|
||||||
"md5": "271b65885d42d174cbc597ca89becb1a",
|
"md5": "271b65885d42d174cbc597ca89becb1a",
|
||||||
|
Loading…
Reference in New Issue
Block a user