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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user