aboutsummaryrefslogtreecommitdiff
path: root/xpdf/Function.cc
diff options
context:
space:
mode:
Diffstat (limited to 'xpdf/Function.cc')
-rw-r--r--xpdf/Function.cc1602
1 files changed, 1602 insertions, 0 deletions
diff --git a/xpdf/Function.cc b/xpdf/Function.cc
new file mode 100644
index 0000000..82f32b9
--- /dev/null
+++ b/xpdf/Function.cc
@@ -0,0 +1,1602 @@
+//========================================================================
+//
+// Function.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "GList.h"
+#include "Object.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Error.h"
+#include "Function.h"
+
+//------------------------------------------------------------------------
+
+// Max depth of nested functions. This is used to catch infinite
+// loops in the function object structure.
+#define recursionLimit 8
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+Function::Function() {
+}
+
+Function::~Function() {
+}
+
+Function *Function::parse(Object *funcObj, int expectedInputs,
+ int expectedOutputs, int recursion) {
+ Function *func;
+ Dict *dict;
+ int funcType;
+ Object obj1;
+
+ if (recursion > recursionLimit) {
+ error(errSyntaxError, -1, "Loop detected in function objects");
+ return NULL;
+ }
+
+ if (funcObj->isStream()) {
+ dict = funcObj->streamGetDict();
+ } else if (funcObj->isDict()) {
+ dict = funcObj->getDict();
+ } else if (funcObj->isName("Identity")) {
+ if (expectedInputs != expectedOutputs) {
+ error(errSyntaxError, -1, "Invalid use of identity function");
+ return NULL;
+ }
+ return new IdentityFunction(expectedInputs);
+ } else {
+ error(errSyntaxError, -1, "Expected function dictionary or stream");
+ return NULL;
+ }
+
+ if (!dict->lookup("FunctionType", &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Function type is missing or wrong type");
+ obj1.free();
+ return NULL;
+ }
+ funcType = obj1.getInt();
+ obj1.free();
+
+ if (funcType == 0) {
+ func = new SampledFunction(funcObj, dict);
+ } else if (funcType == 2) {
+ func = new ExponentialFunction(funcObj, dict);
+ } else if (funcType == 3) {
+ func = new StitchingFunction(funcObj, dict, expectedInputs,
+ expectedOutputs, recursion);
+ } else if (funcType == 4) {
+ func = new PostScriptFunction(funcObj, dict);
+ } else {
+ error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType);
+ return NULL;
+ }
+ if (!func->isOk()) {
+ delete func;
+ return NULL;
+ }
+
+ if (func->getInputSize() != expectedInputs ||
+ (expectedOutputs >= 0 && func->getOutputSize() != expectedOutputs)) {
+ error(errSyntaxError, -1,
+ "Incorrect number of function inputs or outputs");
+ delete func;
+ return NULL;
+ }
+
+ return func;
+}
+
+GBool Function::init(Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ //----- Domain
+ if (!dict->lookup("Domain", &obj1)->isArray()) {
+ error(errSyntaxError, -1, "Function is missing domain");
+ goto err2;
+ }
+ m = obj1.arrayGetLength() / 2;
+ if (m > funcMaxInputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} inputs are unsupported",
+ funcMaxInputs);
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Range
+ hasRange = gFalse;
+ n = 0;
+ if (dict->lookup("Range", &obj1)->isArray()) {
+ hasRange = gTrue;
+ n = obj1.arrayGetLength() / 2;
+ if (n > funcMaxOutputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} outputs are unsupported",
+ funcMaxOutputs);
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ return gTrue;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+ return gFalse;
+}
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+IdentityFunction::IdentityFunction(int nInputs) {
+ int i;
+
+ m = n = nInputs;
+ // domain info shouldn't be used anywhere
+ for (i = 0; i < nInputs; ++i) {
+ domain[i][0] = 0;
+ domain[i][1] = 1;
+ }
+ hasRange = gFalse;
+}
+
+IdentityFunction::~IdentityFunction() {
+}
+
+void IdentityFunction::transform(double *in, double *out) {
+ int i;
+
+ for (i = 0; i < m; ++i) {
+ out[i] = in[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ int sampleBits;
+ double sampleMul;
+ Object obj1, obj2;
+ Guint buf, bitMask;
+ int bits;
+ Guint s;
+ double in[funcMaxInputs];
+ int i, j, t, bit, idx;
+
+ idxOffset = NULL;
+ samples = NULL;
+ sBuf = NULL;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(errSyntaxError, -1, "Type 0 function is missing range");
+ goto err1;
+ }
+ if (m > sampledFuncMaxInputs) {
+ error(errSyntaxError, -1,
+ "Sampled functions with more than {0:d} inputs are unsupported",
+ sampledFuncMaxInputs);
+ goto err1;
+ }
+
+ //----- buffer
+ sBuf = (double *)gmallocn(1 << m, sizeof(double));
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(errSyntaxError, -1, "Type 0 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- Size
+ if (!dict->lookup("Size", &obj1)->isArray() ||
+ obj1.arrayGetLength() != m) {
+ error(errSyntaxError, -1, "Function has missing or invalid size array");
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isInt()) {
+ error(errSyntaxError, -1, "Illegal value in function size array");
+ goto err3;
+ }
+ sampleSize[i] = obj2.getInt();
+ if (sampleSize[i] <= 0) {
+ error(errSyntaxError, -1, "Illegal non-positive value in function size array");
+ goto err3;
+ }
+ obj2.free();
+ }
+ obj1.free();
+ idxOffset = (int *)gmallocn(1 << m, sizeof(int));
+ for (i = 0; i < (1<<m); ++i) {
+ idx = 0;
+ for (j = m - 1, t = i; j >= 1; --j, t <<= 1) {
+ if (sampleSize[j] == 1) {
+ bit = 0;
+ } else {
+ bit = (t >> (m - 1)) & 1;
+ }
+ idx = (idx + bit) * sampleSize[j-1];
+ }
+ if (sampleSize[0] == 1) {
+ bit = 0;
+ } else {
+ bit = (t >> (m - 1)) & 1;
+ }
+ idxOffset[i] = (idx + bit) * n;
+ }
+
+ //----- BitsPerSample
+ if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
+ error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample");
+ goto err2;
+ }
+ sampleBits = obj1.getInt();
+ sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1);
+ obj1.free();
+
+ //----- Encode
+ if (dict->lookup("Encode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*m) {
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < m; ++i) {
+ encode[i][0] = 0;
+ encode[i][1] = sampleSize[i] - 1;
+ }
+ }
+ obj1.free();
+ for (i = 0; i < m; ++i) {
+ inputMul[i] = (encode[i][1] - encode[i][0]) /
+ (domain[i][1] - domain[i][0]);
+ }
+
+ //----- Decode
+ if (dict->lookup("Decode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*n) {
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ decode[i][0] = range[i][0];
+ decode[i][1] = range[i][1];
+ }
+ }
+ obj1.free();
+
+ //----- samples
+ nSamples = n;
+ for (i = 0; i < m; ++i) {
+ if (nSamples > INT_MAX / sampleSize[i]) {
+ error(errSyntaxError, -1, "Integer overflow in sampled function setup");
+ goto err1;
+ }
+ nSamples *= sampleSize[i];
+ }
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ buf = 0;
+ bits = 0;
+ bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU;
+ str->reset();
+ for (i = 0; i < nSamples; ++i) {
+ if (sampleBits == 8) {
+ s = str->getChar();
+ } else if (sampleBits == 16) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ } else if (sampleBits == 32) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ } else {
+ while (bits < sampleBits) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bits += 8;
+ }
+ s = (buf >> (bits - sampleBits)) & bitMask;
+ bits -= sampleBits;
+ }
+ samples[i] = (double)s * sampleMul;
+ }
+ str->close();
+
+ // set up the cache
+ for (i = 0; i < m; ++i) {
+ in[i] = domain[i][0];
+ cacheIn[i] = in[i] - 1;
+ }
+ transform(in, cacheOut);
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+SampledFunction::~SampledFunction() {
+ if (idxOffset) {
+ gfree(idxOffset);
+ }
+ if (samples) {
+ gfree(samples);
+ }
+ if (sBuf) {
+ gfree(sBuf);
+ }
+}
+
+SampledFunction::SampledFunction(SampledFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(SampledFunction));
+ idxOffset = (int *)gmallocn(1 << m, sizeof(int));
+ memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int));
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ memcpy(samples, func->samples, nSamples * sizeof(double));
+ sBuf = (double *)gmallocn(1 << m, sizeof(double));
+}
+
+void SampledFunction::transform(double *in, double *out) {
+ double x;
+ int e[funcMaxInputs];
+ double efrac0[funcMaxInputs];
+ double efrac1[funcMaxInputs];
+ int i, j, k, idx0, t;
+
+ // check the cache
+ for (i = 0; i < m; ++i) {
+ if (in[i] != cacheIn[i]) {
+ break;
+ }
+ }
+ if (i == m) {
+ for (i = 0; i < n; ++i) {
+ out[i] = cacheOut[i];
+ }
+ return;
+ }
+
+ // map input values into sample array
+ for (i = 0; i < m; ++i) {
+ x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
+ if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x)
+ x = 0;
+ } else if (x > sampleSize[i] - 1) {
+ x = sampleSize[i] - 1;
+ }
+ e[i] = (int)x;
+ if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) {
+ // this happens if in[i] = domain[i][1]
+ e[i] = sampleSize[i] - 2;
+ }
+ efrac1[i] = x - e[i];
+ efrac0[i] = 1 - efrac1[i];
+ }
+
+ // compute index for the first sample to be used
+ idx0 = 0;
+ for (k = m - 1; k >= 1; --k) {
+ idx0 = (idx0 + e[k]) * sampleSize[k-1];
+ }
+ idx0 = (idx0 + e[0]) * n;
+
+ // for each output, do m-linear interpolation
+ for (i = 0; i < n; ++i) {
+
+ // pull 2^m values out of the sample array
+ for (j = 0; j < (1<<m); ++j) {
+ sBuf[j] = samples[idx0 + idxOffset[j] + i];
+ }
+
+ // do m sets of interpolations
+ for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
+ for (k = 0; k < t; k += 2) {
+ sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+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];
+ }
+ }
+
+ // save current result in the cache
+ for (i = 0; i < m; ++i) {
+ cacheIn[i] = in[i];
+ }
+ for (i = 0; i < n; ++i) {
+ cacheOut[i] = out[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(errSyntaxError, -1, "Exponential function with more than one input");
+ goto err1;
+ }
+
+ //----- C0
+ if (dict->lookup("C0", &obj1)->isArray()) {
+ if (hasRange && obj1.arrayGetLength() != n) {
+ error(errSyntaxError, -1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = obj1.arrayGetLength();
+ if (n > funcMaxOutputs) {
+ error(errSyntaxError, -1,
+ "Functions with more than {0:d} outputs are unsupported",
+ funcMaxOutputs);
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function C0 array");
+ goto err3;
+ }
+ c0[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (hasRange && n != 1) {
+ error(errSyntaxError, -1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = 1;
+ c0[0] = 0;
+ }
+ obj1.free();
+
+ //----- C1
+ if (dict->lookup("C1", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() != n) {
+ error(errSyntaxError, -1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(errSyntaxError, -1, "Illegal value in function C1 array");
+ goto err3;
+ }
+ c1[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (n != 1) {
+ error(errSyntaxError, -1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ c1[0] = 1;
+ }
+ obj1.free();
+
+ //----- N (exponent)
+ if (!dict->lookup("N", &obj1)->isNum()) {
+ error(errSyntaxError, -1, "Function has missing or invalid N");
+ goto err2;
+ }
+ e = obj1.getNum();
+ obj1.free();
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+ExponentialFunction::~ExponentialFunction() {
+}
+
+ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(ExponentialFunction));
+}
+
+void ExponentialFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < n; ++i) {
+ out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
+ if (hasRange) {
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+ }
+ return;
+}
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict,
+ int expectedInputs, int expectedOutputs,
+ int recursion) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+ funcs = NULL;
+ bounds = NULL;
+ encode = NULL;
+ scale = NULL;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(errSyntaxError, -1, "Stitching function with more than one input");
+ goto err1;
+ }
+
+ //----- Functions
+ if (!dict->lookup("Functions", &obj1)->isArray() ||
+ obj1.arrayGetLength() < 1) {
+ error(errSyntaxError, -1,
+ "Missing 'Functions' entry in stitching function");
+ goto err1;
+ }
+ k = obj1.arrayGetLength();
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ scale = (double *)gmallocn(k, sizeof(double));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = NULL;
+ }
+ for (i = 0; i < k; ++i) {
+ if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2),
+ expectedInputs, expectedOutputs,
+ recursion + 1))) {
+ goto err2;
+ }
+ if (i == 0) {
+ n = funcs[0]->getOutputSize();
+ }
+ if (funcs[i]->getInputSize() != 1 || funcs[i]->getOutputSize() != n) {
+ error(errSyntaxError, -1,
+ "Incompatible subfunctions in stitching function");
+ goto err2;
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Bounds
+ if (!dict->lookup("Bounds", &obj1)->isArray() ||
+ obj1.arrayGetLength() != k - 1) {
+ error(errSyntaxError, -1,
+ "Missing or invalid 'Bounds' entry in stitching function");
+ goto err1;
+ }
+ bounds[0] = domain[0][0];
+ for (i = 1; i < k; ++i) {
+ if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
+ error(errSyntaxError, -1,
+ "Invalid type in 'Bounds' array in stitching function");
+ goto err2;
+ }
+ bounds[i] = obj2.getNum();
+ obj2.free();
+ }
+ bounds[k] = domain[0][1];
+ obj1.free();
+
+ //----- Encode
+ if (!dict->lookup("Encode", &obj1)->isArray() ||
+ obj1.arrayGetLength() != 2 * k) {
+ error(errSyntaxError, -1,
+ "Missing or invalid 'Encode' entry in stitching function");
+ goto err1;
+ }
+ for (i = 0; i < 2 * k; ++i) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ error(errSyntaxError, -1,
+ "Invalid type in 'Encode' array in stitching function");
+ goto err2;
+ }
+ encode[i] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- pre-compute the scale factors
+ for (i = 0; i < k; ++i) {
+ if (bounds[i] == bounds[i+1]) {
+ // avoid a divide-by-zero -- in this situation, function i will
+ // never be used anyway
+ scale[i] = 0;
+ } else {
+ scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]);
+ }
+ }
+
+ ok = gTrue;
+ return;
+
+ err2:
+ obj2.free();
+ err1:
+ obj1.free();
+}
+
+StitchingFunction::StitchingFunction(StitchingFunction *func) {
+ int i;
+
+ memcpy((void *)this, (void *)func, sizeof(StitchingFunction));
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = func->funcs[i]->copy();
+ }
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ memcpy(encode, func->encode, 2 * k * sizeof(double));
+ scale = (double *)gmallocn(k, sizeof(double));
+ memcpy(scale, func->scale, k * sizeof(double));
+ ok = gTrue;
+}
+
+StitchingFunction::~StitchingFunction() {
+ int i;
+
+ if (funcs) {
+ for (i = 0; i < k; ++i) {
+ if (funcs[i]) {
+ delete funcs[i];
+ }
+ }
+ }
+ gfree(funcs);
+ gfree(bounds);
+ gfree(encode);
+ gfree(scale);
+}
+
+void StitchingFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < k - 1; ++i) {
+ if (x < bounds[i+1]) {
+ break;
+ }
+ }
+ x = encode[2*i] + (x - bounds[i]) * scale[i];
+ funcs[i]->transform(&x, out);
+}
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+// This is not an enum, because we can't foreward-declare the enum
+// type in Function.h
+//
+// NB: This must be kept in sync with psOpNames[] below.
+#define psOpAbs 0
+#define psOpAdd 1
+#define psOpAnd 2
+#define psOpAtan 3
+#define psOpBitshift 4
+#define psOpCeiling 5
+#define psOpCopy 6
+#define psOpCos 7
+#define psOpCvi 8
+#define psOpCvr 9
+#define psOpDiv 10
+#define psOpDup 11
+#define psOpEq 12
+#define psOpExch 13
+#define psOpExp 14
+#define psOpFalse 15
+#define psOpFloor 16
+#define psOpGe 17
+#define psOpGt 18
+#define psOpIdiv 19
+#define psOpIndex 20
+#define psOpLe 21
+#define psOpLn 22
+#define psOpLog 23
+#define psOpLt 24
+#define psOpMod 25
+#define psOpMul 26
+#define psOpNe 27
+#define psOpNeg 28
+#define psOpNot 29
+#define psOpOr 30
+#define psOpPop 31
+#define psOpRoll 32
+#define psOpRound 33
+#define psOpSin 34
+#define psOpSqrt 35
+#define psOpSub 36
+#define psOpTrue 37
+#define psOpTruncate 38
+#define psOpXor 39
+// the push/j/jz ops are used internally (and are not listed in psOpNames[])
+#define psOpPush 40
+#define psOpJ 41
+#define psOpJz 42
+
+#define nPSOps (sizeof(psOpNames) / sizeof(const char *))
+
+// Note: 'if' and 'ifelse' are parsed separately.
+// The rest are listed here in alphabetical order.
+//
+// NB: This must be kept in sync with the psOpXXX defines above.
+static const char *psOpNames[] = {
+ "abs",
+ "add",
+ "and",
+ "atan",
+ "bitshift",
+ "ceiling",
+ "copy",
+ "cos",
+ "cvi",
+ "cvr",
+ "div",
+ "dup",
+ "eq",
+ "exch",
+ "exp",
+ "false",
+ "floor",
+ "ge",
+ "gt",
+ "idiv",
+ "index",
+ "le",
+ "ln",
+ "log",
+ "lt",
+ "mod",
+ "mul",
+ "ne",
+ "neg",
+ "not",
+ "or",
+ "pop",
+ "roll",
+ "round",
+ "sin",
+ "sqrt",
+ "sub",
+ "true",
+ "truncate",
+ "xor"
+};
+
+struct PSCode {
+ int op;
+ union {
+ double d;
+ int i;
+ } val;
+};
+
+#define psStackSize 100
+
+PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ GList *tokens;
+ GString *tok;
+ double in[funcMaxInputs];
+ int tokPtr, codePtr, i;
+
+ codeString = NULL;
+ code = NULL;
+ codeSize = 0;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(errSyntaxError, -1, "Type 4 function is missing range");
+ goto err1;
+ }
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(errSyntaxError, -1, "Type 4 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- tokenize the function
+ codeString = new GString();
+ tokens = new GList();
+ str->reset();
+ while ((tok = getToken(str))) {
+ tokens->append(tok);
+ }
+ str->close();
+
+ //----- parse the function
+ if (tokens->getLength() < 1 ||
+ ((GString *)tokens->get(0))->cmp("{")) {
+ error(errSyntaxError, -1, "Expected '{{' at start of PostScript function");
+ goto err2;
+ }
+ tokPtr = 1;
+ codePtr = 0;
+ if (!parseCode(tokens, &tokPtr, &codePtr)) {
+ goto err2;
+ }
+ codeLen = codePtr;
+
+ //----- set up the cache
+ for (i = 0; i < m; ++i) {
+ in[i] = domain[i][0];
+ cacheIn[i] = in[i] - 1;
+ }
+ transform(in, cacheOut);
+
+ ok = gTrue;
+
+ err2:
+ deleteGList(tokens, GString);
+ err1:
+ return;
+}
+
+PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
+ memcpy((void *)this, (void *)func, sizeof(PostScriptFunction));
+ codeString = func->codeString->copy();
+ code = (PSCode *)gmallocn(codeSize, sizeof(PSCode));
+ memcpy(code, func->code, codeSize * sizeof(PSCode));
+}
+
+PostScriptFunction::~PostScriptFunction() {
+ gfree(code);
+ if (codeString) {
+ delete codeString;
+ }
+}
+
+void PostScriptFunction::transform(double *in, double *out) {
+ double stack[psStackSize];
+ double x;
+ int sp, i;
+
+ // check the cache
+ for (i = 0; i < m; ++i) {
+ if (in[i] != cacheIn[i]) {
+ break;
+ }
+ }
+ if (i == m) {
+ for (i = 0; i < n; ++i) {
+ out[i] = cacheOut[i];
+ }
+ return;
+ }
+
+ for (i = 0; i < m; ++i) {
+ stack[psStackSize - 1 - i] = in[i];
+ }
+ sp = exec(stack, psStackSize - m);
+ // if (sp < psStackSize - n) {
+ // error(errSyntaxWarning, -1,
+ // "Extra values on stack at end of PostScript function");
+ // }
+ if (sp > psStackSize - n) {
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ sp = psStackSize - n;
+ }
+ for (i = 0; i < n; ++i) {
+ x = stack[sp + n - 1 - i];
+ if (x < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (x > range[i][1]) {
+ out[i] = range[i][1];
+ } else {
+ out[i] = x;
+ }
+ }
+
+ // save current result in the cache
+ for (i = 0; i < m; ++i) {
+ cacheIn[i] = in[i];
+ }
+ for (i = 0; i < n; ++i) {
+ cacheOut[i] = out[i];
+ }
+}
+
+GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) {
+ GString *tok;
+ char *p;
+ int a, b, mid, cmp;
+ int codePtr0, codePtr1;
+
+ while (1) {
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ p = tok->getCString();
+ if (isdigit(*p) || *p == '.' || *p == '-') {
+ addCodeD(codePtr, psOpPush, atof(tok->getCString()));
+ } else if (!tok->cmp("{")) {
+ codePtr0 = *codePtr;
+ addCodeI(codePtr, psOpJz, 0);
+ if (!parseCode(tokens, tokPtr, codePtr)) {
+ return gFalse;
+ }
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("if")) {
+ code[codePtr0].val.i = *codePtr;
+ } else if (!tok->cmp("{")) {
+ codePtr1 = *codePtr;
+ addCodeI(codePtr, psOpJ, 0);
+ code[codePtr0].val.i = *codePtr;
+ if (!parseCode(tokens, tokPtr, codePtr)) {
+ return gFalse;
+ }
+ if (*tokPtr >= tokens->getLength()) {
+ error(errSyntaxError, -1,
+ "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ tok = (GString *)tokens->get((*tokPtr)++);
+ if (!tok->cmp("ifelse")) {
+ code[codePtr1].val.i = *codePtr;
+ } else {
+ error(errSyntaxError, -1,
+ "Expected 'ifelse' in PostScript function stream");
+ return gFalse;
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Expected 'if' in PostScript function stream");
+ return gFalse;
+ }
+ } else if (!tok->cmp("}")) {
+ break;
+ } else if (!tok->cmp("if")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'if' in PostScript function stream");
+ return gFalse;
+ } else if (!tok->cmp("ifelse")) {
+ error(errSyntaxError, -1,
+ "Unexpected 'ifelse' in PostScript function stream");
+ return gFalse;
+ } else {
+ a = -1;
+ b = nPSOps;
+ cmp = 0; // make gcc happy
+ // invariant: psOpNames[a] < tok < psOpNames[b]
+ while (b - a > 1) {
+ mid = (a + b) / 2;
+ cmp = tok->cmp(psOpNames[mid]);
+ if (cmp > 0) {
+ a = mid;
+ } else if (cmp < 0) {
+ b = mid;
+ } else {
+ a = b = mid;
+ }
+ }
+ if (cmp != 0) {
+ error(errSyntaxError, -1,
+ "Unknown operator '{0:t}' in PostScript function",
+ tok);
+ return gFalse;
+ }
+ addCode(codePtr, a);
+ }
+ }
+ return gTrue;
+}
+
+void PostScriptFunction::addCode(int *codePtr, int op) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeI(int *codePtr, int op, int x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.i = x;
+ ++(*codePtr);
+}
+
+void PostScriptFunction::addCodeD(int *codePtr, int op, double x) {
+ if (*codePtr >= codeSize) {
+ if (codeSize) {
+ codeSize *= 2;
+ } else {
+ codeSize = 16;
+ }
+ code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode));
+ }
+ code[*codePtr].op = op;
+ code[*codePtr].val.d = x;
+ ++(*codePtr);
+}
+
+GString *PostScriptFunction::getToken(Stream *str) {
+ GString *s;
+ int c;
+ GBool comment;
+
+ s = new GString();
+ comment = gFalse;
+ while (1) {
+ if ((c = str->getChar()) == EOF) {
+ delete s;
+ return NULL;
+ }
+ codeString->append((char)c);
+ if (comment) {
+ if (c == '\x0a' || c == '\x0d') {
+ comment = gFalse;
+ }
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (!isspace(c)) {
+ break;
+ }
+ }
+ if (c == '{' || c == '}') {
+ s->append((char)c);
+ } else if (isdigit(c) || c == '.' || c == '-') {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
+ break;
+ }
+ str->getChar();
+ codeString->append((char)c);
+ }
+ } else {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !isalnum(c)) {
+ break;
+ }
+ str->getChar();
+ codeString->append((char)c);
+ }
+ }
+ return s;
+}
+
+int PostScriptFunction::exec(double *stack, int sp0) {
+ PSCode *c;
+ double tmp[psStackSize];
+ double t;
+ int sp, ip, nn, k, i;
+
+ sp = sp0;
+ ip = 0;
+ while (ip < codeLen) {
+ c = &code[ip++];
+ switch(c->op) {
+ case psOpAbs:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = fabs(stack[sp]);
+ break;
+ case psOpAdd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] + stack[sp];
+ ++sp;
+ break;
+ case psOpAnd:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp];
+ ++sp;
+ break;
+ case psOpAtan:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = atan2(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpBitshift:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp + 1];
+ nn = (int)stack[sp];
+ if (nn > 0) {
+ stack[sp + 1] = k << nn;
+ } else if (nn < 0) {
+ stack[sp + 1] = k >> -nn;
+ } else {
+ stack[sp + 1] = k;
+ }
+ ++sp;
+ break;
+ case psOpCeiling:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = ceil(stack[sp]);
+ break;
+ case psOpCopy:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (sp - nn < 0) {
+ goto overflow;
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp - nn + i] = stack[sp + i];
+ }
+ sp -= nn;
+ break;
+ case psOpCos:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = cos(stack[sp]);
+ break;
+ case psOpCvi:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = (int)stack[sp];
+ break;
+ case psOpCvr:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ break;
+ case psOpDiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ if (stack[sp] == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = stack[sp + 1] / stack[sp];
+ ++sp;
+ break;
+ case psOpDup:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = stack[sp];
+ --sp;
+ break;
+ case psOpEq:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpExch:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = stack[sp + 1];
+ stack[sp + 1] = t;
+ break;
+ case psOpExp:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = pow(stack[sp + 1], stack[sp]);
+ ++sp;
+ break;
+ case psOpFalse:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 0;
+ --sp;
+ break;
+ case psOpFloor:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = floor(stack[sp]);
+ break;
+ case psOpGe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpGt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpIdiv:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] / k;
+ ++sp;
+ break;
+ case psOpIndex:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k < 0) {
+ goto invalidArg;
+ }
+ if (sp + 1 + k >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp + 1 + k];
+ break;
+ case psOpLe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpLn:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log(stack[sp]);
+ break;
+ case psOpLog:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = log10(stack[sp]);
+ break;
+ case psOpLt:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpMod:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp];
+ if (k == 0) {
+ goto invalidArg;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] % k;
+ ++sp;
+ break;
+ case psOpMul:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] * stack[sp];
+ ++sp;
+ break;
+ case psOpNe:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0;
+ ++sp;
+ break;
+ case psOpNeg:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = -stack[sp];
+ break;
+ case psOpNot:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = stack[sp] == 0 ? 1 : 0;
+ break;
+ case psOpOr:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPop:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ ++sp;
+ break;
+ case psOpRoll:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ nn = (int)stack[sp++];
+ if (nn < 0) {
+ goto invalidArg;
+ }
+ if (nn > 0) {
+ if (sp + nn > psStackSize) {
+ goto underflow;
+ }
+ if (k >= 0) {
+ k %= nn;
+ } else {
+ k = -k % nn;
+ if (k) {
+ k = nn - k;
+ }
+ }
+ for (i = 0; i < nn; ++i) {
+ tmp[i] = stack[sp + i];
+ }
+ for (i = 0; i < nn; ++i) {
+ stack[sp + i] = tmp[(i + k) % nn];
+ }
+ }
+ break;
+ case psOpRound:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5);
+ break;
+ case psOpSin:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sin(stack[sp]);
+ break;
+ case psOpSqrt:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp] = sqrt(stack[sp]);
+ break;
+ case psOpSub:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = stack[sp + 1] - stack[sp];
+ ++sp;
+ break;
+ case psOpTrue:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[sp - 1] = 1;
+ --sp;
+ break;
+ case psOpTruncate:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ t = stack[sp];
+ stack[sp] = (t >= 0) ? floor(t) : ceil(t);
+ break;
+ case psOpXor:
+ if (sp + 1 >= psStackSize) {
+ goto underflow;
+ }
+ stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp];
+ ++sp;
+ break;
+ case psOpPush:
+ if (sp < 1) {
+ goto overflow;
+ }
+ stack[--sp] = c->val.d;
+ break;
+ case psOpJ:
+ ip = c->val.i;
+ break;
+ case psOpJz:
+ if (sp >= psStackSize) {
+ goto underflow;
+ }
+ k = (int)stack[sp++];
+ if (k == 0) {
+ ip = c->val.i;
+ }
+ break;
+ }
+ }
+ return sp;
+
+ underflow:
+ error(errSyntaxError, -1, "Stack underflow in PostScript function");
+ return sp;
+ overflow:
+ error(errSyntaxError, -1, "Stack overflow in PostScript function");
+ return sp;
+ invalidArg:
+ error(errSyntaxError, -1, "Invalid arg in PostScript function");
+ return sp;
+}