Merge pull request #13754 from Snuffleupagus/refactor-PDFFunction
Remove the IR (internal representation) part of the `PDFFunction` parsing
This commit is contained in:
		
						commit
						e04386c675
					
				@ -131,492 +131,377 @@ function toNumberArray(arr) {
 | 
				
			|||||||
  return arr;
 | 
					  return arr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PDFFunction = (function PDFFunctionClosure() {
 | 
					class PDFFunction {
 | 
				
			||||||
  const CONSTRUCT_SAMPLED = 0;
 | 
					  static getSampleArray(size, outputSize, bps, stream) {
 | 
				
			||||||
  const CONSTRUCT_INTERPOLATED = 2;
 | 
					    let i, ii;
 | 
				
			||||||
  const CONSTRUCT_STICHED = 3;
 | 
					    let length = 1;
 | 
				
			||||||
  const CONSTRUCT_POSTSCRIPT = 4;
 | 
					    for (i = 0, ii = size.length; i < ii; i++) {
 | 
				
			||||||
 | 
					      length *= size[i];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    length *= outputSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					    const array = new Array(length);
 | 
				
			||||||
    getSampleArray(size, outputSize, bps, stream) {
 | 
					    let codeSize = 0;
 | 
				
			||||||
      let i, ii;
 | 
					    let codeBuf = 0;
 | 
				
			||||||
      let length = 1;
 | 
					    // 32 is a valid bps so shifting won't work
 | 
				
			||||||
      for (i = 0, ii = size.length; i < ii; i++) {
 | 
					    const sampleMul = 1.0 / (2.0 ** bps - 1);
 | 
				
			||||||
        length *= size[i];
 | 
					
 | 
				
			||||||
 | 
					    const strBytes = stream.getBytes((length * bps + 7) / 8);
 | 
				
			||||||
 | 
					    let strIdx = 0;
 | 
				
			||||||
 | 
					    for (i = 0; i < length; i++) {
 | 
				
			||||||
 | 
					      while (codeSize < bps) {
 | 
				
			||||||
 | 
					        codeBuf <<= 8;
 | 
				
			||||||
 | 
					        codeBuf |= strBytes[strIdx++];
 | 
				
			||||||
 | 
					        codeSize += 8;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      length *= outputSize;
 | 
					      codeSize -= bps;
 | 
				
			||||||
 | 
					      array[i] = (codeBuf >> codeSize) * sampleMul;
 | 
				
			||||||
 | 
					      codeBuf &= (1 << codeSize) - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return array;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const array = new Array(length);
 | 
					  static parse({ xref, isEvalSupported, fn }) {
 | 
				
			||||||
      let codeSize = 0;
 | 
					    const dict = fn.dict || fn;
 | 
				
			||||||
      let codeBuf = 0;
 | 
					    const typeNum = dict.get("FunctionType");
 | 
				
			||||||
      // 32 is a valid bps so shifting won't work
 | 
					 | 
				
			||||||
      const sampleMul = 1.0 / (2.0 ** bps - 1);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const strBytes = stream.getBytes((length * bps + 7) / 8);
 | 
					    switch (typeNum) {
 | 
				
			||||||
      let strIdx = 0;
 | 
					      case 0:
 | 
				
			||||||
      for (i = 0; i < length; i++) {
 | 
					        return this.constructSampled({ xref, isEvalSupported, fn, dict });
 | 
				
			||||||
        while (codeSize < bps) {
 | 
					      case 1:
 | 
				
			||||||
          codeBuf <<= 8;
 | 
					        break;
 | 
				
			||||||
          codeBuf |= strBytes[strIdx++];
 | 
					      case 2:
 | 
				
			||||||
          codeSize += 8;
 | 
					        return this.constructInterpolated({ xref, isEvalSupported, dict });
 | 
				
			||||||
        }
 | 
					      case 3:
 | 
				
			||||||
        codeSize -= bps;
 | 
					        return this.constructStiched({ xref, isEvalSupported, dict });
 | 
				
			||||||
        array[i] = (codeBuf >> codeSize) * sampleMul;
 | 
					      case 4:
 | 
				
			||||||
        codeBuf &= (1 << codeSize) - 1;
 | 
					        return this.constructPostScript({ xref, isEvalSupported, fn, dict });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    throw new FormatError("Unknown type of function");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static parseArray({ xref, isEvalSupported, fnObj }) {
 | 
				
			||||||
 | 
					    if (!Array.isArray(fnObj)) {
 | 
				
			||||||
 | 
					      // not an array -- parsing as regular function
 | 
				
			||||||
 | 
					      return this.parse({ xref, isEvalSupported, fn: fnObj });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fnArray = [];
 | 
				
			||||||
 | 
					    for (let j = 0, jj = fnObj.length; j < jj; j++) {
 | 
				
			||||||
 | 
					      fnArray.push(
 | 
				
			||||||
 | 
					        this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fnObj[j]) })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return function (src, srcOffset, dest, destOffset) {
 | 
				
			||||||
 | 
					      for (let i = 0, ii = fnArray.length; i < ii; i++) {
 | 
				
			||||||
 | 
					        fnArray[i](src, srcOffset, dest, destOffset + i);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return array;
 | 
					    };
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getIR({ xref, isEvalSupported, fn }) {
 | 
					  static constructSampled({ xref, isEvalSupported, fn, dict }) {
 | 
				
			||||||
      let dict = fn.dict;
 | 
					    function toMultiArray(arr) {
 | 
				
			||||||
      if (!dict) {
 | 
					      const inputLength = arr.length;
 | 
				
			||||||
        dict = fn;
 | 
					      const out = [];
 | 
				
			||||||
 | 
					      let index = 0;
 | 
				
			||||||
 | 
					      for (let i = 0; i < inputLength; i += 2) {
 | 
				
			||||||
 | 
					        out[index++] = [arr[i], arr[i + 1]];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // See chapter 3, page 109 of the PDF reference
 | 
				
			||||||
 | 
					    function interpolate(x, xmin, xmax, ymin, ymax) {
 | 
				
			||||||
 | 
					      return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let domain = toNumberArray(dict.getArray("Domain"));
 | 
				
			||||||
 | 
					    let range = toNumberArray(dict.getArray("Range"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!domain || !range) {
 | 
				
			||||||
 | 
					      throw new FormatError("No domain or range");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const inputSize = domain.length / 2;
 | 
				
			||||||
 | 
					    const outputSize = range.length / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    domain = toMultiArray(domain);
 | 
				
			||||||
 | 
					    range = toMultiArray(range);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const size = toNumberArray(dict.getArray("Size"));
 | 
				
			||||||
 | 
					    const bps = dict.get("BitsPerSample");
 | 
				
			||||||
 | 
					    const order = dict.get("Order") || 1;
 | 
				
			||||||
 | 
					    if (order !== 1) {
 | 
				
			||||||
 | 
					      // No description how cubic spline interpolation works in PDF32000:2008
 | 
				
			||||||
 | 
					      // As in poppler, ignoring order, linear interpolation may work as good
 | 
				
			||||||
 | 
					      info("No support for cubic spline interpolation: " + order);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let encode = toNumberArray(dict.getArray("Encode"));
 | 
				
			||||||
 | 
					    if (!encode) {
 | 
				
			||||||
 | 
					      encode = [];
 | 
				
			||||||
 | 
					      for (let i = 0; i < inputSize; ++i) {
 | 
				
			||||||
 | 
					        encode.push([0, size[i] - 1]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      encode = toMultiArray(encode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let decode = toNumberArray(dict.getArray("Decode"));
 | 
				
			||||||
 | 
					    if (!decode) {
 | 
				
			||||||
 | 
					      decode = range;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      decode = toMultiArray(decode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const samples = this.getSampleArray(size, outputSize, bps, fn);
 | 
				
			||||||
 | 
					    // const mask = 2 ** bps - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return function constructSampledFn(src, srcOffset, dest, destOffset) {
 | 
				
			||||||
 | 
					      // See chapter 3, page 110 of the PDF reference.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Building the cube vertices: its part and sample index
 | 
				
			||||||
 | 
					      // http://rjwagner49.com/Mathematics/Interpolation.pdf
 | 
				
			||||||
 | 
					      const cubeVertices = 1 << inputSize;
 | 
				
			||||||
 | 
					      const cubeN = new Float64Array(cubeVertices);
 | 
				
			||||||
 | 
					      const cubeVertex = new Uint32Array(cubeVertices);
 | 
				
			||||||
 | 
					      let i, j;
 | 
				
			||||||
 | 
					      for (j = 0; j < cubeVertices; j++) {
 | 
				
			||||||
 | 
					        cubeN[j] = 1;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const types = [
 | 
					      let k = outputSize,
 | 
				
			||||||
        this.constructSampled,
 | 
					        pos = 1;
 | 
				
			||||||
        null,
 | 
					      // Map x_i to y_j for 0 <= i < m using the sampled function.
 | 
				
			||||||
        this.constructInterpolated,
 | 
					      for (i = 0; i < inputSize; ++i) {
 | 
				
			||||||
        this.constructStiched,
 | 
					        // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
 | 
				
			||||||
        this.constructPostScript,
 | 
					        const domain_2i = domain[i][0];
 | 
				
			||||||
      ];
 | 
					        const domain_2i_1 = domain[i][1];
 | 
				
			||||||
 | 
					        const xi = Math.min(
 | 
				
			||||||
      const typeNum = dict.get("FunctionType");
 | 
					          Math.max(src[srcOffset + i], domain_2i),
 | 
				
			||||||
      const typeFn = types[typeNum];
 | 
					          domain_2i_1
 | 
				
			||||||
      if (!typeFn) {
 | 
					 | 
				
			||||||
        throw new FormatError("Unknown type of function");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return typeFn.call(this, { xref, isEvalSupported, fn, dict });
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fromIR({ xref, isEvalSupported, IR }) {
 | 
					 | 
				
			||||||
      const type = IR[0];
 | 
					 | 
				
			||||||
      switch (type) {
 | 
					 | 
				
			||||||
        case CONSTRUCT_SAMPLED:
 | 
					 | 
				
			||||||
          return this.constructSampledFromIR({ xref, isEvalSupported, IR });
 | 
					 | 
				
			||||||
        case CONSTRUCT_INTERPOLATED:
 | 
					 | 
				
			||||||
          return this.constructInterpolatedFromIR({
 | 
					 | 
				
			||||||
            xref,
 | 
					 | 
				
			||||||
            isEvalSupported,
 | 
					 | 
				
			||||||
            IR,
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
        case CONSTRUCT_STICHED:
 | 
					 | 
				
			||||||
          return this.constructStichedFromIR({ xref, isEvalSupported, IR });
 | 
					 | 
				
			||||||
        // case CONSTRUCT_POSTSCRIPT:
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
          return this.constructPostScriptFromIR({ xref, isEvalSupported, IR });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parse({ xref, isEvalSupported, fn }) {
 | 
					 | 
				
			||||||
      const IR = this.getIR({ xref, isEvalSupported, fn });
 | 
					 | 
				
			||||||
      return this.fromIR({ xref, isEvalSupported, IR });
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parseArray({ xref, isEvalSupported, fnObj }) {
 | 
					 | 
				
			||||||
      if (!Array.isArray(fnObj)) {
 | 
					 | 
				
			||||||
        // not an array -- parsing as regular function
 | 
					 | 
				
			||||||
        return this.parse({ xref, isEvalSupported, fn: fnObj });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const fnArray = [];
 | 
					 | 
				
			||||||
      for (let j = 0, jj = fnObj.length; j < jj; j++) {
 | 
					 | 
				
			||||||
        fnArray.push(
 | 
					 | 
				
			||||||
          this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fnObj[j]) })
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return function (src, srcOffset, dest, destOffset) {
 | 
					 | 
				
			||||||
        for (let i = 0, ii = fnArray.length; i < ii; i++) {
 | 
					 | 
				
			||||||
          fnArray[i](src, srcOffset, dest, destOffset + i);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructSampled({ xref, isEvalSupported, fn, dict }) {
 | 
					        // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
 | 
				
			||||||
      function toMultiArray(arr) {
 | 
					        //                   Encode_2i, Encode_2i+1)
 | 
				
			||||||
        const inputLength = arr.length;
 | 
					        let e = interpolate(
 | 
				
			||||||
        const out = [];
 | 
					          xi,
 | 
				
			||||||
        let index = 0;
 | 
					          domain_2i,
 | 
				
			||||||
        for (let i = 0; i < inputLength; i += 2) {
 | 
					          domain_2i_1,
 | 
				
			||||||
          out[index] = [arr[i], arr[i + 1]];
 | 
					          encode[i][0],
 | 
				
			||||||
          ++index;
 | 
					          encode[i][1]
 | 
				
			||||||
        }
 | 
					        );
 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      let domain = toNumberArray(dict.getArray("Domain"));
 | 
					 | 
				
			||||||
      let range = toNumberArray(dict.getArray("Range"));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!domain || !range) {
 | 
					        // e_i' = min(max(e_i, 0), Size_i - 1)
 | 
				
			||||||
        throw new FormatError("No domain or range");
 | 
					        const size_i = size[i];
 | 
				
			||||||
      }
 | 
					        e = Math.min(Math.max(e, 0), size_i - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const inputSize = domain.length / 2;
 | 
					        // Adjusting the cube: N and vertex sample index
 | 
				
			||||||
      const outputSize = range.length / 2;
 | 
					        const e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
 | 
				
			||||||
 | 
					        const n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
 | 
				
			||||||
      domain = toMultiArray(domain);
 | 
					        const n1 = e - e0; // (e - e0) / (e1 - e0);
 | 
				
			||||||
      range = toMultiArray(range);
 | 
					        const offset0 = e0 * k;
 | 
				
			||||||
 | 
					        const offset1 = offset0 + k; // e1 * k
 | 
				
			||||||
      const size = toNumberArray(dict.getArray("Size"));
 | 
					 | 
				
			||||||
      const bps = dict.get("BitsPerSample");
 | 
					 | 
				
			||||||
      const order = dict.get("Order") || 1;
 | 
					 | 
				
			||||||
      if (order !== 1) {
 | 
					 | 
				
			||||||
        // No description how cubic spline interpolation works in PDF32000:2008
 | 
					 | 
				
			||||||
        // As in poppler, ignoring order, linear interpolation may work as good
 | 
					 | 
				
			||||||
        info("No support for cubic spline interpolation: " + order);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      let encode = toNumberArray(dict.getArray("Encode"));
 | 
					 | 
				
			||||||
      if (!encode) {
 | 
					 | 
				
			||||||
        encode = [];
 | 
					 | 
				
			||||||
        for (let i = 0; i < inputSize; ++i) {
 | 
					 | 
				
			||||||
          encode.push([0, size[i] - 1]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        encode = toMultiArray(encode);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      let decode = toNumberArray(dict.getArray("Decode"));
 | 
					 | 
				
			||||||
      if (!decode) {
 | 
					 | 
				
			||||||
        decode = range;
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        decode = toMultiArray(decode);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const samples = this.getSampleArray(size, outputSize, bps, fn);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return [
 | 
					 | 
				
			||||||
        CONSTRUCT_SAMPLED,
 | 
					 | 
				
			||||||
        inputSize,
 | 
					 | 
				
			||||||
        domain,
 | 
					 | 
				
			||||||
        encode,
 | 
					 | 
				
			||||||
        decode,
 | 
					 | 
				
			||||||
        samples,
 | 
					 | 
				
			||||||
        size,
 | 
					 | 
				
			||||||
        outputSize,
 | 
					 | 
				
			||||||
        2 ** bps - 1,
 | 
					 | 
				
			||||||
        range,
 | 
					 | 
				
			||||||
      ];
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructSampledFromIR({ xref, isEvalSupported, IR }) {
 | 
					 | 
				
			||||||
      // See chapter 3, page 109 of the PDF reference
 | 
					 | 
				
			||||||
      function interpolate(x, xmin, xmax, ymin, ymax) {
 | 
					 | 
				
			||||||
        return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return function constructSampledFromIRResult(
 | 
					 | 
				
			||||||
        src,
 | 
					 | 
				
			||||||
        srcOffset,
 | 
					 | 
				
			||||||
        dest,
 | 
					 | 
				
			||||||
        destOffset
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        // See chapter 3, page 110 of the PDF reference.
 | 
					 | 
				
			||||||
        const m = IR[1];
 | 
					 | 
				
			||||||
        const domain = IR[2];
 | 
					 | 
				
			||||||
        const encode = IR[3];
 | 
					 | 
				
			||||||
        const decode = IR[4];
 | 
					 | 
				
			||||||
        const samples = IR[5];
 | 
					 | 
				
			||||||
        const size = IR[6];
 | 
					 | 
				
			||||||
        const n = IR[7];
 | 
					 | 
				
			||||||
        // var mask = IR[8];
 | 
					 | 
				
			||||||
        const range = IR[9];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Building the cube vertices: its part and sample index
 | 
					 | 
				
			||||||
        // http://rjwagner49.com/Mathematics/Interpolation.pdf
 | 
					 | 
				
			||||||
        const cubeVertices = 1 << m;
 | 
					 | 
				
			||||||
        const cubeN = new Float64Array(cubeVertices);
 | 
					 | 
				
			||||||
        const cubeVertex = new Uint32Array(cubeVertices);
 | 
					 | 
				
			||||||
        let i, j;
 | 
					 | 
				
			||||||
        for (j = 0; j < cubeVertices; j++) {
 | 
					        for (j = 0; j < cubeVertices; j++) {
 | 
				
			||||||
          cubeN[j] = 1;
 | 
					          if (j & pos) {
 | 
				
			||||||
        }
 | 
					            cubeN[j] *= n1;
 | 
				
			||||||
 | 
					            cubeVertex[j] += offset1;
 | 
				
			||||||
        let k = n,
 | 
					 | 
				
			||||||
          pos = 1;
 | 
					 | 
				
			||||||
        // Map x_i to y_j for 0 <= i < m using the sampled function.
 | 
					 | 
				
			||||||
        for (i = 0; i < m; ++i) {
 | 
					 | 
				
			||||||
          // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
 | 
					 | 
				
			||||||
          const domain_2i = domain[i][0];
 | 
					 | 
				
			||||||
          const domain_2i_1 = domain[i][1];
 | 
					 | 
				
			||||||
          const xi = Math.min(
 | 
					 | 
				
			||||||
            Math.max(src[srcOffset + i], domain_2i),
 | 
					 | 
				
			||||||
            domain_2i_1
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
 | 
					 | 
				
			||||||
          //                   Encode_2i, Encode_2i+1)
 | 
					 | 
				
			||||||
          let e = interpolate(
 | 
					 | 
				
			||||||
            xi,
 | 
					 | 
				
			||||||
            domain_2i,
 | 
					 | 
				
			||||||
            domain_2i_1,
 | 
					 | 
				
			||||||
            encode[i][0],
 | 
					 | 
				
			||||||
            encode[i][1]
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // e_i' = min(max(e_i, 0), Size_i - 1)
 | 
					 | 
				
			||||||
          const size_i = size[i];
 | 
					 | 
				
			||||||
          e = Math.min(Math.max(e, 0), size_i - 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // Adjusting the cube: N and vertex sample index
 | 
					 | 
				
			||||||
          const e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
 | 
					 | 
				
			||||||
          const n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
 | 
					 | 
				
			||||||
          const n1 = e - e0; // (e - e0) / (e1 - e0);
 | 
					 | 
				
			||||||
          const offset0 = e0 * k;
 | 
					 | 
				
			||||||
          const offset1 = offset0 + k; // e1 * k
 | 
					 | 
				
			||||||
          for (j = 0; j < cubeVertices; j++) {
 | 
					 | 
				
			||||||
            if (j & pos) {
 | 
					 | 
				
			||||||
              cubeN[j] *= n1;
 | 
					 | 
				
			||||||
              cubeVertex[j] += offset1;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              cubeN[j] *= n0;
 | 
					 | 
				
			||||||
              cubeVertex[j] += offset0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          k *= size_i;
 | 
					 | 
				
			||||||
          pos <<= 1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (j = 0; j < n; ++j) {
 | 
					 | 
				
			||||||
          // Sum all cube vertices' samples portions
 | 
					 | 
				
			||||||
          let rj = 0;
 | 
					 | 
				
			||||||
          for (i = 0; i < cubeVertices; i++) {
 | 
					 | 
				
			||||||
            rj += samples[cubeVertex[i] + j] * cubeN[i];
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
 | 
					 | 
				
			||||||
          //                    Decode_2j, Decode_2j+1)
 | 
					 | 
				
			||||||
          rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // y_j = min(max(r_j, range_2j), range_2j+1)
 | 
					 | 
				
			||||||
          dest[destOffset + j] = Math.min(
 | 
					 | 
				
			||||||
            Math.max(rj, range[j][0]),
 | 
					 | 
				
			||||||
            range[j][1]
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructInterpolated({ xref, isEvalSupported, fn, dict }) {
 | 
					 | 
				
			||||||
      const c0 = toNumberArray(dict.getArray("C0")) || [0];
 | 
					 | 
				
			||||||
      const c1 = toNumberArray(dict.getArray("C1")) || [1];
 | 
					 | 
				
			||||||
      const n = dict.get("N");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const length = c0.length;
 | 
					 | 
				
			||||||
      const diff = [];
 | 
					 | 
				
			||||||
      for (let i = 0; i < length; ++i) {
 | 
					 | 
				
			||||||
        diff.push(c1[i] - c0[i]);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return [CONSTRUCT_INTERPOLATED, c0, diff, n];
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructInterpolatedFromIR({ xref, isEvalSupported, IR }) {
 | 
					 | 
				
			||||||
      const c0 = IR[1];
 | 
					 | 
				
			||||||
      const diff = IR[2];
 | 
					 | 
				
			||||||
      const n = IR[3];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const length = diff.length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return function constructInterpolatedFromIRResult(
 | 
					 | 
				
			||||||
        src,
 | 
					 | 
				
			||||||
        srcOffset,
 | 
					 | 
				
			||||||
        dest,
 | 
					 | 
				
			||||||
        destOffset
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (let j = 0; j < length; ++j) {
 | 
					 | 
				
			||||||
          dest[destOffset + j] = c0[j] + x * diff[j];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructStiched({ xref, isEvalSupported, fn, dict }) {
 | 
					 | 
				
			||||||
      const domain = toNumberArray(dict.getArray("Domain"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (!domain) {
 | 
					 | 
				
			||||||
        throw new FormatError("No domain");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const inputSize = domain.length / 2;
 | 
					 | 
				
			||||||
      if (inputSize !== 1) {
 | 
					 | 
				
			||||||
        throw new FormatError("Bad domain for stiched function");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const fnRefs = dict.get("Functions");
 | 
					 | 
				
			||||||
      const fns = [];
 | 
					 | 
				
			||||||
      for (let i = 0, ii = fnRefs.length; i < ii; ++i) {
 | 
					 | 
				
			||||||
        fns.push(
 | 
					 | 
				
			||||||
          this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fnRefs[i]) })
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const bounds = toNumberArray(dict.getArray("Bounds"));
 | 
					 | 
				
			||||||
      const encode = toNumberArray(dict.getArray("Encode"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructStichedFromIR({ xref, isEvalSupported, IR }) {
 | 
					 | 
				
			||||||
      const domain = IR[1];
 | 
					 | 
				
			||||||
      const bounds = IR[2];
 | 
					 | 
				
			||||||
      const encode = IR[3];
 | 
					 | 
				
			||||||
      const fns = IR[4];
 | 
					 | 
				
			||||||
      const tmpBuf = new Float32Array(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return function constructStichedFromIRResult(
 | 
					 | 
				
			||||||
        src,
 | 
					 | 
				
			||||||
        srcOffset,
 | 
					 | 
				
			||||||
        dest,
 | 
					 | 
				
			||||||
        destOffset
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        const clip = function constructStichedFromIRClip(v, min, max) {
 | 
					 | 
				
			||||||
          if (v > max) {
 | 
					 | 
				
			||||||
            v = max;
 | 
					 | 
				
			||||||
          } else if (v < min) {
 | 
					 | 
				
			||||||
            v = min;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          return v;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // clip to domain
 | 
					 | 
				
			||||||
        const v = clip(src[srcOffset], domain[0], domain[1]);
 | 
					 | 
				
			||||||
        // calculate which bound the value is in
 | 
					 | 
				
			||||||
        const length = bounds.length;
 | 
					 | 
				
			||||||
        let i;
 | 
					 | 
				
			||||||
        for (i = 0; i < length; ++i) {
 | 
					 | 
				
			||||||
          if (v < bounds[i]) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // encode value into domain of function
 | 
					 | 
				
			||||||
        let dmin = domain[0];
 | 
					 | 
				
			||||||
        if (i > 0) {
 | 
					 | 
				
			||||||
          dmin = bounds[i - 1];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        let dmax = domain[1];
 | 
					 | 
				
			||||||
        if (i < bounds.length) {
 | 
					 | 
				
			||||||
          dmax = bounds[i];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const rmin = encode[2 * i];
 | 
					 | 
				
			||||||
        const rmax = encode[2 * i + 1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Prevent the value from becoming NaN as a result
 | 
					 | 
				
			||||||
        // of division by zero (fixes issue6113.pdf).
 | 
					 | 
				
			||||||
        tmpBuf[0] =
 | 
					 | 
				
			||||||
          dmin === dmax
 | 
					 | 
				
			||||||
            ? rmin
 | 
					 | 
				
			||||||
            : rmin + ((v - dmin) * (rmax - rmin)) / (dmax - dmin);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // call the appropriate function
 | 
					 | 
				
			||||||
        fns[i](tmpBuf, 0, dest, destOffset);
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructPostScript({ xref, isEvalSupported, fn, dict }) {
 | 
					 | 
				
			||||||
      const domain = toNumberArray(dict.getArray("Domain"));
 | 
					 | 
				
			||||||
      const range = toNumberArray(dict.getArray("Range"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (!domain) {
 | 
					 | 
				
			||||||
        throw new FormatError("No domain.");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (!range) {
 | 
					 | 
				
			||||||
        throw new FormatError("No range.");
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const lexer = new PostScriptLexer(fn);
 | 
					 | 
				
			||||||
      const parser = new PostScriptParser(lexer);
 | 
					 | 
				
			||||||
      const code = parser.parse();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return [CONSTRUCT_POSTSCRIPT, domain, range, code];
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructPostScriptFromIR({ xref, isEvalSupported, IR }) {
 | 
					 | 
				
			||||||
      const domain = IR[1];
 | 
					 | 
				
			||||||
      const range = IR[2];
 | 
					 | 
				
			||||||
      const code = IR[3];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (isEvalSupported && IsEvalSupportedCached.value) {
 | 
					 | 
				
			||||||
        const compiled = new PostScriptCompiler().compile(code, domain, range);
 | 
					 | 
				
			||||||
        if (compiled) {
 | 
					 | 
				
			||||||
          // Compiled function consists of simple expressions such as addition,
 | 
					 | 
				
			||||||
          // subtraction, Math.max, and also contains 'var' and 'return'
 | 
					 | 
				
			||||||
          // statements. See the generation in the PostScriptCompiler below.
 | 
					 | 
				
			||||||
          // eslint-disable-next-line no-new-func
 | 
					 | 
				
			||||||
          return new Function(
 | 
					 | 
				
			||||||
            "src",
 | 
					 | 
				
			||||||
            "srcOffset",
 | 
					 | 
				
			||||||
            "dest",
 | 
					 | 
				
			||||||
            "destOffset",
 | 
					 | 
				
			||||||
            compiled
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      info("Unable to compile PS function");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const numOutputs = range.length >> 1;
 | 
					 | 
				
			||||||
      const numInputs = domain.length >> 1;
 | 
					 | 
				
			||||||
      const evaluator = new PostScriptEvaluator(code);
 | 
					 | 
				
			||||||
      // Cache the values for a big speed up, the cache size is limited though
 | 
					 | 
				
			||||||
      // since the number of possible values can be huge from a PS function.
 | 
					 | 
				
			||||||
      const cache = Object.create(null);
 | 
					 | 
				
			||||||
      // The MAX_CACHE_SIZE is set to ~4x the maximum number of distinct values
 | 
					 | 
				
			||||||
      // seen in our tests.
 | 
					 | 
				
			||||||
      const MAX_CACHE_SIZE = 2048 * 4;
 | 
					 | 
				
			||||||
      let cache_available = MAX_CACHE_SIZE;
 | 
					 | 
				
			||||||
      const tmpBuf = new Float32Array(numInputs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return function constructPostScriptFromIRResult(
 | 
					 | 
				
			||||||
        src,
 | 
					 | 
				
			||||||
        srcOffset,
 | 
					 | 
				
			||||||
        dest,
 | 
					 | 
				
			||||||
        destOffset
 | 
					 | 
				
			||||||
      ) {
 | 
					 | 
				
			||||||
        let i, value;
 | 
					 | 
				
			||||||
        let key = "";
 | 
					 | 
				
			||||||
        const input = tmpBuf;
 | 
					 | 
				
			||||||
        for (i = 0; i < numInputs; i++) {
 | 
					 | 
				
			||||||
          value = src[srcOffset + i];
 | 
					 | 
				
			||||||
          input[i] = value;
 | 
					 | 
				
			||||||
          key += value + "_";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const cachedValue = cache[key];
 | 
					 | 
				
			||||||
        if (cachedValue !== undefined) {
 | 
					 | 
				
			||||||
          dest.set(cachedValue, destOffset);
 | 
					 | 
				
			||||||
          return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const output = new Float32Array(numOutputs);
 | 
					 | 
				
			||||||
        const stack = evaluator.execute(input);
 | 
					 | 
				
			||||||
        const stackIndex = stack.length - numOutputs;
 | 
					 | 
				
			||||||
        for (i = 0; i < numOutputs; i++) {
 | 
					 | 
				
			||||||
          value = stack[stackIndex + i];
 | 
					 | 
				
			||||||
          let bound = range[i * 2];
 | 
					 | 
				
			||||||
          if (value < bound) {
 | 
					 | 
				
			||||||
            value = bound;
 | 
					 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            bound = range[i * 2 + 1];
 | 
					            cubeN[j] *= n0;
 | 
				
			||||||
            if (value > bound) {
 | 
					            cubeVertex[j] += offset0;
 | 
				
			||||||
              value = bound;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          output[i] = value;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (cache_available > 0) {
 | 
					
 | 
				
			||||||
          cache_available--;
 | 
					        k *= size_i;
 | 
				
			||||||
          cache[key] = output;
 | 
					        pos <<= 1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (j = 0; j < outputSize; ++j) {
 | 
				
			||||||
 | 
					        // Sum all cube vertices' samples portions
 | 
				
			||||||
 | 
					        let rj = 0;
 | 
				
			||||||
 | 
					        for (i = 0; i < cubeVertices; i++) {
 | 
				
			||||||
 | 
					          rj += samples[cubeVertex[i] + j] * cubeN[i];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        dest.set(output, destOffset);
 | 
					
 | 
				
			||||||
 | 
					        // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
 | 
				
			||||||
 | 
					        //                    Decode_2j, Decode_2j+1)
 | 
				
			||||||
 | 
					        rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // y_j = min(max(r_j, range_2j), range_2j+1)
 | 
				
			||||||
 | 
					        dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static constructInterpolated({ xref, isEvalSupported, dict }) {
 | 
				
			||||||
 | 
					    const c0 = toNumberArray(dict.getArray("C0")) || [0];
 | 
				
			||||||
 | 
					    const c1 = toNumberArray(dict.getArray("C1")) || [1];
 | 
				
			||||||
 | 
					    const n = dict.get("N");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const diff = [];
 | 
				
			||||||
 | 
					    for (let i = 0, ii = c0.length; i < ii; ++i) {
 | 
				
			||||||
 | 
					      diff.push(c1[i] - c0[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const length = diff.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return function constructInterpolatedFn(src, srcOffset, dest, destOffset) {
 | 
				
			||||||
 | 
					      const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (let j = 0; j < length; ++j) {
 | 
				
			||||||
 | 
					        dest[destOffset + j] = c0[j] + x * diff[j];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static constructStiched({ xref, isEvalSupported, dict }) {
 | 
				
			||||||
 | 
					    const domain = toNumberArray(dict.getArray("Domain"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!domain) {
 | 
				
			||||||
 | 
					      throw new FormatError("No domain");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const inputSize = domain.length / 2;
 | 
				
			||||||
 | 
					    if (inputSize !== 1) {
 | 
				
			||||||
 | 
					      throw new FormatError("Bad domain for stiched function");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fnRefs = dict.get("Functions");
 | 
				
			||||||
 | 
					    const fns = [];
 | 
				
			||||||
 | 
					    for (let i = 0, ii = fnRefs.length; i < ii; ++i) {
 | 
				
			||||||
 | 
					      fns.push(
 | 
				
			||||||
 | 
					        this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fnRefs[i]) })
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const bounds = toNumberArray(dict.getArray("Bounds"));
 | 
				
			||||||
 | 
					    const encode = toNumberArray(dict.getArray("Encode"));
 | 
				
			||||||
 | 
					    const tmpBuf = new Float32Array(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return function constructStichedFn(src, srcOffset, dest, destOffset) {
 | 
				
			||||||
 | 
					      const clip = function constructStichedFromIRClip(v, min, max) {
 | 
				
			||||||
 | 
					        if (v > max) {
 | 
				
			||||||
 | 
					          v = max;
 | 
				
			||||||
 | 
					        } else if (v < min) {
 | 
				
			||||||
 | 
					          v = min;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return v;
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    },
 | 
					
 | 
				
			||||||
  };
 | 
					      // clip to domain
 | 
				
			||||||
})();
 | 
					      const v = clip(src[srcOffset], domain[0], domain[1]);
 | 
				
			||||||
 | 
					      // calculate which bound the value is in
 | 
				
			||||||
 | 
					      const length = bounds.length;
 | 
				
			||||||
 | 
					      let i;
 | 
				
			||||||
 | 
					      for (i = 0; i < length; ++i) {
 | 
				
			||||||
 | 
					        if (v < bounds[i]) {
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // encode value into domain of function
 | 
				
			||||||
 | 
					      let dmin = domain[0];
 | 
				
			||||||
 | 
					      if (i > 0) {
 | 
				
			||||||
 | 
					        dmin = bounds[i - 1];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let dmax = domain[1];
 | 
				
			||||||
 | 
					      if (i < bounds.length) {
 | 
				
			||||||
 | 
					        dmax = bounds[i];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const rmin = encode[2 * i];
 | 
				
			||||||
 | 
					      const rmax = encode[2 * i + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Prevent the value from becoming NaN as a result
 | 
				
			||||||
 | 
					      // of division by zero (fixes issue6113.pdf).
 | 
				
			||||||
 | 
					      tmpBuf[0] =
 | 
				
			||||||
 | 
					        dmin === dmax
 | 
				
			||||||
 | 
					          ? rmin
 | 
				
			||||||
 | 
					          : rmin + ((v - dmin) * (rmax - rmin)) / (dmax - dmin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // call the appropriate function
 | 
				
			||||||
 | 
					      fns[i](tmpBuf, 0, dest, destOffset);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static constructPostScript({ xref, isEvalSupported, fn, dict }) {
 | 
				
			||||||
 | 
					    const domain = toNumberArray(dict.getArray("Domain"));
 | 
				
			||||||
 | 
					    const range = toNumberArray(dict.getArray("Range"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!domain) {
 | 
				
			||||||
 | 
					      throw new FormatError("No domain.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!range) {
 | 
				
			||||||
 | 
					      throw new FormatError("No range.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const lexer = new PostScriptLexer(fn);
 | 
				
			||||||
 | 
					    const parser = new PostScriptParser(lexer);
 | 
				
			||||||
 | 
					    const code = parser.parse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isEvalSupported && IsEvalSupportedCached.value) {
 | 
				
			||||||
 | 
					      const compiled = new PostScriptCompiler().compile(code, domain, range);
 | 
				
			||||||
 | 
					      if (compiled) {
 | 
				
			||||||
 | 
					        // Compiled function consists of simple expressions such as addition,
 | 
				
			||||||
 | 
					        // subtraction, Math.max, and also contains 'var' and 'return'
 | 
				
			||||||
 | 
					        // statements. See the generation in the PostScriptCompiler below.
 | 
				
			||||||
 | 
					        // eslint-disable-next-line no-new-func
 | 
				
			||||||
 | 
					        return new Function("src", "srcOffset", "dest", "destOffset", compiled);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    info("Unable to compile PS function");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const numOutputs = range.length >> 1;
 | 
				
			||||||
 | 
					    const numInputs = domain.length >> 1;
 | 
				
			||||||
 | 
					    const evaluator = new PostScriptEvaluator(code);
 | 
				
			||||||
 | 
					    // Cache the values for a big speed up, the cache size is limited though
 | 
				
			||||||
 | 
					    // since the number of possible values can be huge from a PS function.
 | 
				
			||||||
 | 
					    const cache = Object.create(null);
 | 
				
			||||||
 | 
					    // The MAX_CACHE_SIZE is set to ~4x the maximum number of distinct values
 | 
				
			||||||
 | 
					    // seen in our tests.
 | 
				
			||||||
 | 
					    const MAX_CACHE_SIZE = 2048 * 4;
 | 
				
			||||||
 | 
					    let cache_available = MAX_CACHE_SIZE;
 | 
				
			||||||
 | 
					    const tmpBuf = new Float32Array(numInputs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return function constructPostScriptFn(src, srcOffset, dest, destOffset) {
 | 
				
			||||||
 | 
					      let i, value;
 | 
				
			||||||
 | 
					      let key = "";
 | 
				
			||||||
 | 
					      const input = tmpBuf;
 | 
				
			||||||
 | 
					      for (i = 0; i < numInputs; i++) {
 | 
				
			||||||
 | 
					        value = src[srcOffset + i];
 | 
				
			||||||
 | 
					        input[i] = value;
 | 
				
			||||||
 | 
					        key += value + "_";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const cachedValue = cache[key];
 | 
				
			||||||
 | 
					      if (cachedValue !== undefined) {
 | 
				
			||||||
 | 
					        dest.set(cachedValue, destOffset);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const output = new Float32Array(numOutputs);
 | 
				
			||||||
 | 
					      const stack = evaluator.execute(input);
 | 
				
			||||||
 | 
					      const stackIndex = stack.length - numOutputs;
 | 
				
			||||||
 | 
					      for (i = 0; i < numOutputs; i++) {
 | 
				
			||||||
 | 
					        value = stack[stackIndex + i];
 | 
				
			||||||
 | 
					        let bound = range[i * 2];
 | 
				
			||||||
 | 
					        if (value < bound) {
 | 
				
			||||||
 | 
					          value = bound;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          bound = range[i * 2 + 1];
 | 
				
			||||||
 | 
					          if (value > bound) {
 | 
				
			||||||
 | 
					            value = bound;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        output[i] = value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (cache_available > 0) {
 | 
				
			||||||
 | 
					        cache_available--;
 | 
				
			||||||
 | 
					        cache[key] = output;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      dest.set(output, destOffset);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function isPDFFunction(v) {
 | 
					function isPDFFunction(v) {
 | 
				
			||||||
  let fnDict;
 | 
					  let fnDict;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user