Fix where image component decoding takes place.

This commit is contained in:
Brendan Dahl 2013-03-14 14:06:44 -07:00
parent f627281626
commit 93b99e7352
3 changed files with 61 additions and 41 deletions

View File

@ -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);

View File

@ -0,0 +1 @@
https://bugzilla.mozilla.org/attachment.cgi?id=718826

View File

@ -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",