From 543e3377de27e9281fd7bef52f9fd9945ef74b08 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 11 Nov 2011 14:44:47 -0800 Subject: [PATCH] Adding multi dimensional interpolation and test file. --- src/function.js | 142 ++++++++++++++++++++++++++-------------- test/pdfs/.gitignore | 1 + test/pdfs/devicen.pdf | Bin 0 -> 9550 bytes test/test_manifest.json | 7 ++ 4 files changed, 100 insertions(+), 50 deletions(-) create mode 100644 test/pdfs/devicen.pdf diff --git a/src/function.js b/src/function.js index 80c5a5460..2f9b050dd 100644 --- a/src/function.js +++ b/src/function.js @@ -20,6 +20,7 @@ var PDFFunction = (function pdfFunction() { var array = []; var codeSize = 0; var codeBuf = 0; + var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1); var strBytes = str.getBytes((length * bps + 7) / 8); var strIdx = 0; @@ -30,7 +31,7 @@ var PDFFunction = (function pdfFunction() { codeSize += 8; } codeSize -= bps; - array.push(codeBuf >> codeSize); + array.push((codeBuf >> codeSize) * sampleMul); codeBuf &= (1 << codeSize) - 1; } return array; @@ -76,6 +77,17 @@ var PDFFunction = (function pdfFunction() { }, constructSampled: function pdfFunctionConstructSampled(str, dict) { + function toMultiArray(arr) { + var inputLength = arr.length; + var outputLength = arr.length / 2; + var out = new Array(outputLength); + var index = 0; + for (var i = 0; i < inputLength; i += 2) { + out[index] = [arr[i], arr[i + 1]]; + ++index; + } + return out; + } var domain = dict.get('Domain'); var range = dict.get('Range'); @@ -85,9 +97,8 @@ var PDFFunction = (function pdfFunction() { var inputSize = domain.length / 2; var outputSize = range.length / 2; - if (inputSize != 1) - error('No support for multi-variable inputs to functions: ' + - inputSize); + domain = toMultiArray(domain); + range = toMultiArray(range); var size = dict.get('Size'); var bps = dict.get('BitsPerSample'); @@ -105,15 +116,36 @@ var PDFFunction = (function pdfFunction() { encode.push(size[i] - 1); } } + encode = toMultiArray(encode); + var decode = dict.get('Decode'); if (!decode) decode = range; + else + decode = toMultiArray(decode); + + // Precalc the multipliers + var inputMul = new Float64Array(inputSize); + for (var i = 0; i < inputSize; ++i) { + inputMul[i] = (encode[i][1] - encode[i][0]) / + (domain[i][1] - domain[i][0]); + } + + var idxMul = new Int32Array(inputSize); + idxMul[0] = outputSize; + for (i = 1; i < inputSize; ++i) { + idxMul[i] = idxMul[i - 1] * size[i - 1]; + } + + var nSamples = outputSize; + for (i = 0; i < inputSize; ++i) + nSamples *= size[i]; var samples = this.getSampleArray(size, outputSize, bps, str); return [ CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, - outputSize, bps, range + outputSize, bps, range, inputMul, idxMul, nSamples ]; }, @@ -127,64 +159,74 @@ var PDFFunction = (function pdfFunction() { var outputSize = IR[7]; var bps = IR[8]; var range = IR[9]; + var inputMul = IR[10]; + var idxMul = IR[11]; + var nSamples = IR[12]; return function constructSampledFromIRResult(args) { - var clip = function constructSampledFromIRClip(v, min, max) { - if (v > max) - v = max; - else if (v < min) - v = min; - return v; - }; - if (inputSize != args.length) error('Incorrect number of arguments: ' + inputSize + ' != ' + args.length); + // Most of the below is a port of Poppler's implementation. + // TODO: There's a few other ways to do multilinear interpolation such + // as piecewise, which is much faster but an approximation. + var out = new Float64Array(outputSize); + var x; + var e = new Array(inputSize); + var efrac0 = new Float64Array(inputSize); + var efrac1 = new Float64Array(inputSize); + var sBuf = new Float64Array(1 << inputSize); + var i, j, k, idx, t; - for (var i = 0; i < inputSize; i++) { - var i2 = i * 2; - - // clip to the domain - var v = clip(args[i], domain[i2], domain[i2 + 1]); - - // encode - v = encode[i2] + ((v - domain[i2]) * - (encode[i2 + 1] - encode[i2]) / - (domain[i2 + 1] - domain[i2])); - - // clip to the size - args[i] = clip(v, 0, size[i] - 1); + // map input values into sample array + for (i = 0; i < inputSize; ++i) { + x = (args[i] - domain[i][0]) * inputMul[i] + encode[i][0]; + if (x < 0) { + x = 0; + } else if (x > size[i] - 1) { + x = size[i] - 1; + } + e[i] = [Math.floor(x), 0]; + if ((e[i][1] = e[i][0] + 1) >= size[i]) { + // this happens if in[i] = domain[i][1] + e[i][1] = e[i][0]; + } + efrac1[i] = x - e[i][0]; + efrac0[i] = 1 - efrac1[i]; } - // interpolate to table - TODO('Multi-dimensional interpolation'); - var floor = Math.floor(args[0]); - var ceil = Math.ceil(args[0]); - var scale = args[0] - floor; + // for each output, do m-linear interpolation + for (i = 0; i < outputSize; ++i) { - floor *= outputSize; - ceil *= outputSize; - - var output = [], v = 0; - for (var i = 0; i < outputSize; ++i) { - if (ceil == floor) { - v = samples[ceil + i]; - } else { - var low = samples[floor + i]; - var high = samples[ceil + i]; - v = low * scale + high * (1 - scale); + // pull 2^m values out of the sample array + for (j = 0; j < (1 << inputSize); ++j) { + idx = i; + for (k = 0, t = j; k < inputSize; ++k, t >>= 1) { + idx += idxMul[k] * (e[k][t & 1]); + } + if (idx >= 0 && idx < nSamples) { + sBuf[j] = samples[idx]; + } else { + sBuf[j] = 0; // TODO Investigate if this is what Adobe does + } } - var i2 = i * 2; - // decode - v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) / - ((1 << bps) - 1)); + // do m sets of interpolations + for (j = 0, t = (1 << inputSize); j < inputSize; ++j, t >>= 1) { + for (k = 0; k < t; k += 2) { + sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k + 1]; + } + } - // clip to the domain - output.push(clip(v, range[i2], range[i2 + 1])); + // map output value to range + out[i] = (sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]); + if (out[i] < range[i][0]) { + out[i] = range[i][0]; + } else if (out[i] > range[i][1]) { + out[i] = range[i][1]; + } } - - return output; + return out; } }, diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 443cb155a..ac3dabb70 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -14,4 +14,5 @@ !sizes.pdf !close-path-bug.pdf !alphatrans.pdf +!devicen.pdf diff --git a/test/pdfs/devicen.pdf b/test/pdfs/devicen.pdf new file mode 100644 index 0000000000000000000000000000000000000000..53ef9e5d5174b4d333c81ef8f79d22d17a5fc001 GIT binary patch literal 9550 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 literal 0 HcmV?d00001 diff --git a/test/test_manifest.json b/test/test_manifest.json index 8085506a2..4d2c859b5 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -268,5 +268,12 @@ "link": false, "rounds": 1, "type": "eq" + }, + { "id": "devicen", + "file": "pdfs/devicen.pdf", + "md5": "b9dbf00ec6bf02fcbefbb40332713aad", + "link": false, + "rounds": 1, + "type": "eq" } ]