Merge pull request #4565 from fkaelberer/fixJPXparsing

Read color info from JPX stream + fix color problem #4540
This commit is contained in:
Yury Delendik 2014-04-07 09:32:03 -05:00
commit 31c260a886
2 changed files with 93 additions and 40 deletions

View File

@ -14,8 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals ColorSpace, DecodeStream, error, isArray, ImageKind, isStream,
JpegStream, Name, Promise, Stream, warn, LegacyPromise */
/* globals ColorSpace, DecodeStream, error, info, isArray, ImageKind, isStream,
JpegStream, JpxImage, Name, Promise, Stream, warn, LegacyPromise */
'use strict';
@ -51,16 +51,23 @@ var PDFImage = (function PDFImageClosure() {
}
function PDFImage(xref, res, image, inline, smask, mask, isMask) {
this.image = image;
if (image.getParams) {
// JPX/JPEG2000 streams directly contain bits per component
// and color space mode information.
warn('get params from actual stream');
// var bits = ...
// var colorspace = ...
var dict = image.dict;
if (dict.has('Filter')) {
var filter = dict.get('Filter').name;
if (filter === 'JPXDecode') {
info('get image params from JPX stream');
var jpxImage = new JpxImage();
jpxImage.parseImageProperties(image.stream);
image.stream.reset();
image.bitsPerComponent = jpxImage.bitsPerComponent;
image.numComps = jpxImage.componentsCount;
} else if (filter === 'JBIG2Decode') {
image.bitsPerComponent = 1;
image.numComps = 1;
}
}
// TODO cache rendered images?
var dict = image.dict;
this.width = dict.get('Width', 'W');
this.height = dict.get('Height', 'H');
@ -89,8 +96,19 @@ var PDFImage = (function PDFImageClosure() {
if (!this.imageMask) {
var colorSpace = dict.get('ColorSpace', 'CS');
if (!colorSpace) {
warn('JPX images (which do not require color spaces)');
colorSpace = Name.get('DeviceRGB');
info('JPX images (which do not require color spaces)');
switch (image.numComps) {
case 1:
colorSpace = Name.get('DeviceGray');
break;
case 3:
colorSpace = Name.get('DeviceRGB');
break;
default:
// TODO: Find out how four color channels are handled. CMYK? Alpha?
error('JPX images with ' + this.numComps +
' color components not supported.');
}
}
this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
this.numComps = this.colorSpace.numComps;

View File

@ -26,10 +26,6 @@ var JpxImage = (function JpxImageClosure() {
'HL': 1,
'HH': 2
};
var TransformType = {
IRREVERSIBLE: 0,
REVERSIBLE: 1
};
function JpxImage() {
this.failOnCorruptedImage = false;
}
@ -102,6 +98,39 @@ var JpxImage = (function JpxImageClosure() {
}
}
},
parseImageProperties: function JpxImage_parseImageProperties(stream) {
try {
var newByte = stream.getByte();
while (newByte >= 0) {
var oldByte = newByte;
newByte = stream.getByte();
var code = (oldByte << 8) | newByte;
// Image and tile size (SIZ)
if (code == 0xFF51) {
stream.skip(4);
var Xsiz = stream.getUint32(); // Byte 4
var Ysiz = stream.getUint32(); // Byte 8
var XOsiz = stream.getUint32(); // Byte 12
var YOsiz = stream.getUint32(); // Byte 16
stream.skip(16);
var Csiz = stream.getUint16(); // Byte 36
this.width = Xsiz - XOsiz;
this.height = Ysiz - YOsiz;
this.componentsCount = Csiz;
// Results are always returned as UInt8Arrays
this.bitsPerComponent = 8;
return;
}
}
throw 'No size marker found in JPX stream';
} catch (e) {
if (this.failOnCorruptedImage) {
error('JPX error: ' + e);
} else {
warn('JPX error: ' + e + '. Trying to recover');
}
}
},
parseCodestream: function JpxImage_parseCodestream(data, start, end) {
var context = {};
try {
@ -270,7 +299,7 @@ var JpxImage = (function JpxImageClosure() {
cod.verticalyStripe = !!(blockStyle & 8);
cod.predictableTermination = !!(blockStyle & 16);
cod.segmentationSymbolUsed = !!(blockStyle & 32);
cod.transformation = data[j++];
cod.reversibleTransformation = data[j++];
if (cod.entropyCoderWithCustomPrecincts) {
var precinctsSizes = [];
while (j < length + position) {
@ -333,6 +362,8 @@ var JpxImage = (function JpxImageClosure() {
length = readUint16(data, position);
// skipping content
break;
case 0xFF53: // Coding style component (COC)
throw 'Codestream code 0xFF53 (COC) is not implemented';
default:
throw 'Unknown codestream code: ' + code.toString(16);
}
@ -878,7 +909,7 @@ var JpxImage = (function JpxImageClosure() {
return position;
}
function copyCoefficients(coefficients, x0, y0, width, height,
delta, mb, codeblocks, transformation,
delta, mb, codeblocks, reversible,
segmentationSymbolUsed) {
for (var i = 0, ii = codeblocks.length; i < ii; ++i) {
var codeblock = codeblocks[i];
@ -934,16 +965,22 @@ var JpxImage = (function JpxImageClosure() {
var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width;
var n, nb, correction, position = 0;
var irreversible = (transformation === TransformType.IRREVERSIBLE);
var irreversible = !reversible;
var sign = bitModel.coefficentsSign;
var magnitude = bitModel.coefficentsMagnitude;
var bitsDecoded = bitModel.bitsDecoded;
var magnitudeCorrection = reversible ? 0 : 0.5;
for (var j = 0; j < blockHeight; j++) {
for (var k = 0; k < blockWidth; k++) {
n = (sign[position] ? -1 : 1) * magnitude[position];
nb = bitsDecoded[position];
correction = (irreversible || mb > nb) ? 1 << (mb - nb) : 1;
coefficients[offset++] = n * correction * delta;
var mag = magnitude[position];
if (mag !== 0) {
n = sign[position] ? -(mag + magnitudeCorrection) :
(mag + magnitudeCorrection);
nb = bitsDecoded[position];
correction = (irreversible || mb > nb) ? 1 << (mb - nb) : 1;
coefficients[offset] = n * correction * delta;
}
offset++;
position++;
}
offset += width - blockWidth;
@ -959,16 +996,15 @@ var JpxImage = (function JpxImageClosure() {
var spqcds = quantizationParameters.SPqcds;
var scalarExpounded = quantizationParameters.scalarExpounded;
var guardBits = quantizationParameters.guardBits;
var transformation = codingStyleParameters.transformation;
var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed;
var precision = context.components[c].precision;
var transformation = codingStyleParameters.transformation;
var transform = (transformation === TransformType.IRREVERSIBLE ?
new IrreversibleTransform() : new ReversibleTransform());
var reversible = codingStyleParameters.reversibleTransformation;
var transform = (reversible ? new ReversibleTransform() :
new IrreversibleTransform());
var subbandCoefficients = [];
var k = 0, b = 0;
var b = 0;
for (var i = 0; i <= decompositionLevelsCount; i++) {
var resolution = component.resolutions[i];
@ -989,13 +1025,13 @@ var JpxImage = (function JpxImageClosure() {
var gainLog2 = SubbandsGainLog2[subband.type];
// calulate quantization coefficient (Section E.1.1.1)
var delta = (transformation === TransformType.IRREVERSIBLE ?
Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048) : 1);
var delta = (reversible ? 1 :
Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048));
var mb = (guardBits + epsilon - 1);
var coefficients = new Float32Array(width * height);
copyCoefficients(coefficients, subband.tbx0, subband.tby0,
width, height, delta, mb, subband.codeblocks, transformation,
width, height, delta, mb, subband.codeblocks, reversible,
segmentationSymbolUsed);
subbandCoefficients.push({
@ -1034,8 +1070,7 @@ var JpxImage = (function JpxImageClosure() {
// Section G.2.2 Inverse multi component transform
if (tile.codingStyleDefaultParameters.multipleComponentTransform) {
var component0 = tile.components[0];
var transformation = component0.codingStyleParameters.transformation;
if (transformation === TransformType.IRREVERSIBLE) {
if (!component0.codingStyleParameters.reversibleTransformation) {
// inverse irreversible multiple component transform
var y0items = result[0].items;
var y1items = result[1].items;
@ -1628,26 +1663,26 @@ var JpxImage = (function JpxImageClosure() {
var items = new Float32Array(width * height);
var i, j, k, l;
for (i = 0; i < llHeight; i++) {
var k = i * llWidth, l = i * 2 * width;
for (i = 0, k = 0; i < llHeight; i++) {
l = i * 2 * width;
for (var j = 0; j < llWidth; j++, k++, l += 2) {
items[l] = llItems[k];
}
}
for (i = 0; i < hlHeight; i++) {
k = i * hlWidth; l = i * 2 * width + 1;
for (i = 0, k = 0; i < hlHeight; i++) {
l = i * 2 * width + 1;
for (j = 0; j < hlWidth; j++, k++, l += 2) {
items[l] = hlItems[k];
}
}
for (i = 0; i < lhHeight; i++) {
k = i * lhWidth; l = (i * 2 + 1) * width;
for (i = 0, k = 0; i < lhHeight; i++) {
l = (i * 2 + 1) * width;
for (j = 0; j < lhWidth; j++, k++, l += 2) {
items[l] = lhItems[k];
}
}
for (i = 0; i < hhHeight; i++) {
k = i * hhWidth; l = (i * 2 + 1) * width + 1;
for (i = 0, k = 0; i < hhHeight; i++) {
l = (i * 2 + 1) * width + 1;
for (j = 0; j < hhWidth; j++, k++, l += 2) {
items[l] = hhItems[k];
}