Merge pull request #4565 from fkaelberer/fixJPXparsing
Read color info from JPX stream + fix color problem #4540
This commit is contained in:
commit
31c260a886
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user