X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=pdf2swf%2Fxpdf%2FFunction.cc;fp=pdf2swf%2Fxpdf%2FFunction.cc;h=64ea60c1902e6bbbcc8b7e3e3b0ef21ceb993fa6;hb=3df3a8d12d7a516579a4eae276b077c015df0f70;hp=0000000000000000000000000000000000000000;hpb=50dd339d3d6262763616efe8d7ee415ab19befb9;p=swftools.git diff --git a/pdf2swf/xpdf/Function.cc b/pdf2swf/xpdf/Function.cc new file mode 100644 index 0000000..64ea60c --- /dev/null +++ b/pdf2swf/xpdf/Function.cc @@ -0,0 +1,1520 @@ +//======================================================================== +// +// Function.cc +// +// Copyright 2001-2002 Glyph & Cog, LLC +// +//======================================================================== + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include +#include +#include +#include +#include +#include "gmem.h" +#include "Object.h" +#include "Dict.h" +#include "Stream.h" +#include "Error.h" +#include "Function.h" + +//------------------------------------------------------------------------ +// Function +//------------------------------------------------------------------------ + +Function::Function() { +} + +Function::~Function() { +} + +Function *Function::parse(Object *funcObj) { + Function *func; + Dict *dict; + int funcType; + Object obj1; + + if (funcObj->isStream()) { + dict = funcObj->streamGetDict(); + } else if (funcObj->isDict()) { + dict = funcObj->getDict(); + } else if (funcObj->isName("Identity")) { + return new IdentityFunction(); + } else { + error(-1, "Expected function dictionary or stream"); + return NULL; + } + + if (!dict->lookup("FunctionType", &obj1)->isInt()) { + error(-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); + } else if (funcType == 4) { + func = new PostScriptFunction(funcObj, dict); + } else { + error(-1, "Unimplemented function type (%d)", funcType); + return NULL; + } + if (!func->isOk()) { + delete func; + return NULL; + } + + return func; +} + +GBool Function::init(Dict *dict) { + Object obj1, obj2; + int i; + + //----- Domain + if (!dict->lookup("Domain", &obj1)->isArray()) { + error(-1, "Function is missing domain"); + goto err2; + } + m = obj1.arrayGetLength() / 2; + if (m > funcMaxInputs) { + error(-1, "Functions with more than %d inputs are unsupported", + funcMaxInputs); + goto err2; + } + for (i = 0; i < m; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(-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(-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(-1, "Functions with more than %d outputs are unsupported", + funcMaxOutputs); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(-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(-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 i; + + // fill these in with arbitrary values just in case they get used + // somewhere + m = funcMaxInputs; + n = funcMaxOutputs; + for (i = 0; i < funcMaxInputs; ++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 < funcMaxOutputs; ++i) { + out[i] = in[i]; + } +} + +//------------------------------------------------------------------------ +// SampledFunction +//------------------------------------------------------------------------ + +SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { + Stream *str; + int nSamples, sampleBits; + double sampleMul; + Object obj1, obj2; + Guint buf, bitMask; + int bits; + int s; + int i; + + samples = NULL; + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (!hasRange) { + error(-1, "Type 0 function is missing range"); + goto err1; + } + + //----- get the stream + if (!funcObj->isStream()) { + error(-1, "Type 0 function isn't a stream"); + goto err1; + } + str = funcObj->getStream(); + + //----- Size + if (!dict->lookup("Size", &obj1)->isArray() || + obj1.arrayGetLength() != m) { + error(-1, "Function has missing or invalid size array"); + goto err2; + } + for (i = 0; i < m; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isInt()) { + error(-1, "Illegal value in function size array"); + goto err3; + } + sampleSize[i] = obj2.getInt(); + obj2.free(); + } + obj1.free(); + + //----- BitsPerSample + if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { + error(-1, "Function has missing or invalid BitsPerSample"); + goto err2; + } + sampleBits = obj1.getInt(); + sampleMul = 1.0 / (double)((1 << 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(-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(-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(); + + //----- 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(-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(-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) + nSamples *= sampleSize[i]; + samples = (double *)gmalloc(nSamples * sizeof(double)); + buf = 0; + bits = 0; + bitMask = (1 << sampleBits) - 1; + 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(); + + ok = gTrue; + return; + + err3: + obj2.free(); + err2: + obj1.free(); + err1: + return; +} + +SampledFunction::~SampledFunction() { + if (samples) { + gfree(samples); + } +} + +SampledFunction::SampledFunction(SampledFunction *func) { + int nSamples, i; + + memcpy(this, func, sizeof(SampledFunction)); + + nSamples = n; + for (i = 0; i < m; ++i) { + nSamples *= sampleSize[i]; + } + samples = (double *)gmalloc(nSamples * sizeof(double)); + memcpy(samples, func->samples, nSamples * sizeof(double)); +} + +void SampledFunction::transform(double *in, double *out) { + double x; + int e[2][funcMaxInputs]; + double efrac[funcMaxInputs]; + double s0[1 << funcMaxInputs], s1[1 << funcMaxInputs]; + int i, j, k, idx; + + // map input values into sample array + for (i = 0; i < m; ++i) { + x = ((in[i] - domain[i][0]) / (domain[i][1] - domain[i][0])) * + (encode[i][1] - encode[i][0]) + encode[i][0]; + if (x < 0) { + x = 0; + } else if (x > sampleSize[i] - 1) { + x = sampleSize[i] - 1; + } + e[0][i] = (int)floor(x); + e[1][i] = (int)ceil(x); + efrac[i] = x - e[0][i]; + } + + // 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<= 0; --k) { + idx = idx * sampleSize[k] + e[(j >> k) & 1][k]; + } + idx = idx * n + i; + s0[j] = samples[idx]; + } + + // do m sets of interpolations + for (j = 0; j < m; ++j) { + for (k = 0; k < (1 << (m - j)); k += 2) { + s1[k >> 1] = (1 - efrac[j]) * s0[k] + efrac[j] * s0[k+1]; + } + memcpy(s0, s1, (1 << (m - j - 1)) * sizeof(double)); + } + + // map output value to range + out[i] = s0[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]; + } + } +} + +//------------------------------------------------------------------------ +// ExponentialFunction +//------------------------------------------------------------------------ + +ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { + Object obj1, obj2; + GBool hasN; + int i; + + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (m != 1) { + error(-1, "Exponential function with more than one input"); + goto err1; + } + hasN = hasRange; + + //----- default values + for (i = 0; i < funcMaxOutputs; ++i) { + c0[i] = 0; + c1[i] = 1; + } + + //----- C0 + if (dict->lookup("C0", &obj1)->isArray()) { + if (!hasN) { + n = obj1.arrayGetLength(); + hasN = gTrue; + } else if (obj1.arrayGetLength() != n) { + error(-1, "Function's C0 array is wrong length"); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isNum()) { + error(-1, "Illegal value in function C0 array"); + goto err3; + } + c0[i] = obj2.getNum(); + obj2.free(); + } + } + obj1.free(); + + //----- C1 + if (dict->lookup("C1", &obj1)->isArray()) { + if (!hasN) { + n = obj1.arrayGetLength(); + hasN = gTrue; + } else if (obj1.arrayGetLength() != n) { + error(-1, "Function's C1 array is wrong length"); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isNum()) { + error(-1, "Illegal value in function C1 array"); + goto err3; + } + c1[i] = obj2.getNum(); + obj2.free(); + } + } + obj1.free(); + + //----- N (exponent) + if (!dict->lookup("N", &obj1)->isNum()) { + error(-1, "Function has missing or invalid N"); + goto err2; + } + e = obj1.getNum(); + obj1.free(); + + // this isn't supposed to happen, but I've run into (broken) PDF + // files where it does + if (!hasN) { + error(-1, "Exponential function does not define number of output values"); + n = 1; + } + + ok = gTrue; + return; + + err3: + obj2.free(); + err2: + obj1.free(); + err1: + return; +} + +ExponentialFunction::~ExponentialFunction() { +} + +ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { + memcpy(this, 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) { + Object obj1, obj2; + int i; + + ok = gFalse; + funcs = NULL; + bounds = NULL; + encode = NULL; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (m != 1) { + error(-1, "Stitching function with more than one input"); + goto err1; + } + + //----- Functions + if (!dict->lookup("Functions", &obj1)->isArray()) { + error(-1, "Missing 'Functions' entry in stitching function"); + goto err1; + } + k = obj1.arrayGetLength(); + funcs = (Function **)gmalloc(k * sizeof(Function *)); + bounds = (double *)gmalloc((k + 1) * sizeof(double)); + encode = (double *)gmalloc(2 * 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)))) { + goto err2; + } + if (i > 0 && (funcs[i]->getInputSize() != 1 || + funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { + error(-1, "Incompatible subfunctions in stitching function"); + goto err2; + } + obj2.free(); + } + obj1.free(); + + //----- Bounds + if (!dict->lookup("Bounds", &obj1)->isArray() || + obj1.arrayGetLength() != k - 1) { + error(-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(-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(-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(-1, "Invalid type in 'Encode' array in stitching function"); + goto err2; + } + encode[i] = obj2.getNum(); + obj2.free(); + } + obj1.free(); + + ok = gTrue; + return; + + err2: + obj2.free(); + err1: + obj1.free(); +} + +StitchingFunction::StitchingFunction(StitchingFunction *func) { + k = func->k; + funcs = (Function **)gmalloc(k * sizeof(Function *)); + memcpy(funcs, func->funcs, k * sizeof(Function *)); + bounds = (double *)gmalloc((k + 1) * sizeof(double)); + memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); + encode = (double *)gmalloc(2 * k * sizeof(double)); + memcpy(encode, func->encode, 2 * k * sizeof(double)); + ok = gTrue; +} + +StitchingFunction::~StitchingFunction() { + int i; + + for (i = 0; i < k; ++i) { + if (funcs[i]) { + delete funcs[i]; + } + } + gfree(funcs); + gfree(bounds); + gfree(encode); +} + +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]) / (bounds[i+1] - bounds[i])) * + (encode[2*i+1] - encode[2*i]); + funcs[i]->transform(&x, out); +} + +//------------------------------------------------------------------------ +// PostScriptFunction +//------------------------------------------------------------------------ + +enum PSOp { + psOpAbs, + psOpAdd, + psOpAnd, + psOpAtan, + psOpBitshift, + psOpCeiling, + psOpCopy, + psOpCos, + psOpCvi, + psOpCvr, + psOpDiv, + psOpDup, + psOpEq, + psOpExch, + psOpExp, + psOpFalse, + psOpFloor, + psOpGe, + psOpGt, + psOpIdiv, + psOpIndex, + psOpLe, + psOpLn, + psOpLog, + psOpLt, + psOpMod, + psOpMul, + psOpNe, + psOpNeg, + psOpNot, + psOpOr, + psOpPop, + psOpRoll, + psOpRound, + psOpSin, + psOpSqrt, + psOpSub, + psOpTrue, + psOpTruncate, + psOpXor, + psOpIf, + psOpIfelse, + psOpReturn +}; + +// Note: 'if' and 'ifelse' are parsed separately. +// The rest are listed here in alphabetical order. +// The index in this table is equivalent to the entry in PSOp. +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" +}; + +#define nPSOps (sizeof(psOpNames) / sizeof(char *)) + +enum PSObjectType { + psBool, + psInt, + psReal, + psOperator, + psBlock +}; + +// In the code array, 'if'/'ifelse' operators take up three slots +// plus space for the code in the subclause(s). +// +// +---------------------------------+ +// | psOperator: psOpIf / psOpIfelse | +// +---------------------------------+ +// | psBlock: ptr= | +// +---------------------------------+ +// | psBlock: ptr= | +// +---------------------------------+ +// | if clause | +// | ... | +// | psOperator: psOpReturn | +// +---------------------------------+ +// | else clause | +// | ... | +// | psOperator: psOpReturn | +// +---------------------------------+ +// | ... | +// +// For 'if', pointer is present in the code stream but unused. + +struct PSObject { + PSObjectType type; + union { + GBool booln; // boolean (stack only) + int intg; // integer (stack and code) + double real; // real (stack and code) + PSOp op; // operator (code only) + int blk; // if/ifelse block pointer (code only) + }; +}; + +#define psStackSize 100 + +class PSStack { +public: + + PSStack() { sp = psStackSize; } + void pushBool(GBool booln); + void pushInt(int intg); + void pushReal(double real); + GBool popBool(); + int popInt(); + double popNum(); + GBool empty() { return sp == psStackSize; } + GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; } + GBool topTwoAreInts() + { return sp < psStackSize - 1 && + stack[sp].type == psInt && + stack[sp+1].type == psInt; } + GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; } + GBool topTwoAreNums() + { return sp < psStackSize - 1 && + (stack[sp].type == psInt || stack[sp].type == psReal) && + (stack[sp+1].type == psInt || stack[sp+1].type == psReal); } + void copy(int n); + void roll(int n, int j); + void index(int i); + void pop(); + +private: + + GBool checkOverflow(int n = 1); + GBool checkUnderflow(); + GBool checkType(PSObjectType t1, PSObjectType t2); + + PSObject stack[psStackSize]; + int sp; +}; + +GBool PSStack::checkOverflow(int n) { + if (sp - n < 0) { + error(-1, "Stack overflow in PostScript function"); + return gFalse; + } + return gTrue; +} + +GBool PSStack::checkUnderflow() { + if (sp == psStackSize) { + error(-1, "Stack underflow in PostScript function"); + return gFalse; + } + return gTrue; +} + +GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) { + if (stack[sp].type != t1 && stack[sp].type != t2) { + error(-1, "Type mismatch in PostScript function"); + return gFalse; + } + return gTrue; +} + +void PSStack::pushBool(GBool booln) { + if (checkOverflow()) { + stack[--sp].type = psBool; + stack[sp].booln = booln; + } +} + +void PSStack::pushInt(int intg) { + if (checkOverflow()) { + stack[--sp].type = psInt; + stack[sp].intg = intg; + } +} + +void PSStack::pushReal(double real) { + if (checkOverflow()) { + stack[--sp].type = psReal; + stack[sp].real = real; + } +} + +GBool PSStack::popBool() { + if (checkUnderflow() && checkType(psBool, psBool)) { + return stack[sp++].booln; + } + return gFalse; +} + +int PSStack::popInt() { + if (checkUnderflow() && checkType(psInt, psInt)) { + return stack[sp++].intg; + } + return 0; +} + +double PSStack::popNum() { + double ret; + + if (checkUnderflow() && checkType(psInt, psReal)) { + ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real; + ++sp; + return ret; + } + return 0; +} + +void PSStack::copy(int n) { + int i; + + if (!checkOverflow(n)) { + return; + } + for (i = sp + n - 1; i <= sp; ++i) { + stack[i - n] = stack[i]; + } + sp -= n; +} + +void PSStack::roll(int n, int j) { + PSObject obj; + int i, k; + + if (j >= 0) { + j %= n; + } else { + j = -j % n; + if (j != 0) { + j = n - j; + } + } + if (n <= 0 || j == 0) { + return; + } + for (i = 0; i < j; ++i) { + obj = stack[sp]; + for (k = sp; k < sp + n - 1; ++k) { + stack[k] = stack[k+1]; + } + stack[sp + n - 1] = obj; + } +} + +void PSStack::index(int i) { + if (!checkOverflow()) { + return; + } + --sp; + stack[sp] = stack[sp + 1 + i]; +} + +void PSStack::pop() { + if (!checkUnderflow()) { + return; + } + ++sp; +} + +PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { + Stream *str; + int codePtr; + GString *tok; + + code = NULL; + codeSize = 0; + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (!hasRange) { + error(-1, "Type 4 function is missing range"); + goto err1; + } + + //----- get the stream + if (!funcObj->isStream()) { + error(-1, "Type 4 function isn't a stream"); + goto err1; + } + str = funcObj->getStream(); + + //----- parse the function + str->reset(); + if (!(tok = getToken(str)) || tok->cmp("{")) { + error(-1, "Expected '{' at start of PostScript function"); + if (tok) { + delete tok; + } + goto err1; + } + delete tok; + codePtr = 0; + if (!parseCode(str, &codePtr)) { + goto err2; + } + str->close(); + + ok = gTrue; + + err2: + str->close(); + err1: + return; +} + +PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { + memcpy(this, func, sizeof(PostScriptFunction)); + code = (PSObject *)gmalloc(codeSize * sizeof(PSObject)); + memcpy(code, func->code, codeSize * sizeof(PSObject)); +} + +PostScriptFunction::~PostScriptFunction() { + gfree(code); +} + +void PostScriptFunction::transform(double *in, double *out) { + PSStack *stack; + int i; + + stack = new PSStack(); + for (i = 0; i < m; ++i) { + //~ may need to check for integers here + stack->pushReal(in[i]); + } + exec(stack, 0); + for (i = n - 1; i >= 0; --i) { + out[i] = stack->popNum(); + if (out[i] < range[i][0]) { + out[i] = range[i][0]; + } else if (out[i] > range[i][1]) { + out[i] = range[i][1]; + } + } + // if (!stack->empty()) { + // error(-1, "Extra values on stack at end of PostScript function"); + // } + delete stack; +} + +GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) { + GString *tok; + char *p; + GBool isReal; + int opPtr, elsePtr; + int a, b, mid, cmp; + + while (1) { + if (!(tok = getToken(str))) { + error(-1, "Unexpected end of PostScript function stream"); + return gFalse; + } + p = tok->getCString(); + if (isdigit(*p) || *p == '.' || *p == '-') { + isReal = gFalse; + for (++p; *p; ++p) { + if (*p == '.') { + isReal = gTrue; + break; + } + } + resizeCode(*codePtr); + if (isReal) { + code[*codePtr].type = psReal; + code[*codePtr].real = atof(tok->getCString()); + } else { + code[*codePtr].type = psInt; + code[*codePtr].intg = atoi(tok->getCString()); + } + ++*codePtr; + delete tok; + } else if (!tok->cmp("{")) { + delete tok; + opPtr = *codePtr; + *codePtr += 3; + resizeCode(opPtr + 2); + if (!parseCode(str, codePtr)) { + return gFalse; + } + if (!(tok = getToken(str))) { + error(-1, "Unexpected end of PostScript function stream"); + return gFalse; + } + if (!tok->cmp("{")) { + elsePtr = *codePtr; + if (!parseCode(str, codePtr)) { + return gFalse; + } + } else { + elsePtr = -1; + } + delete tok; + if (!(tok = getToken(str))) { + error(-1, "Unexpected end of PostScript function stream"); + return gFalse; + } + if (!tok->cmp("if")) { + if (elsePtr >= 0) { + error(-1, "Got 'if' operator with two blocks in PostScript function"); + return gFalse; + } + code[opPtr].type = psOperator; + code[opPtr].op = psOpIf; + code[opPtr+2].type = psBlock; + code[opPtr+2].blk = *codePtr; + } else if (!tok->cmp("ifelse")) { + if (elsePtr < 0) { + error(-1, "Got 'ifelse' operator with one blocks in PostScript function"); + return gFalse; + } + code[opPtr].type = psOperator; + code[opPtr].op = psOpIfelse; + code[opPtr+1].type = psBlock; + code[opPtr+1].blk = elsePtr; + code[opPtr+2].type = psBlock; + code[opPtr+2].blk = *codePtr; + } else { + error(-1, "Expected if/ifelse operator in PostScript function"); + delete tok; + return gFalse; + } + delete tok; + } else if (!tok->cmp("}")) { + delete tok; + resizeCode(*codePtr); + code[*codePtr].type = psOperator; + code[*codePtr].op = psOpReturn; + ++*codePtr; + break; + } else { + a = -1; + b = nPSOps; + // 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(-1, "Unknown operator '%s' in PostScript function", + tok->getCString()); + delete tok; + return gFalse; + } + delete tok; + resizeCode(*codePtr); + code[*codePtr].type = psOperator; + code[*codePtr].op = (PSOp)a; + ++*codePtr; + } + } + return gTrue; +} + +GString *PostScriptFunction::getToken(Stream *str) { + GString *s; + int c; + + s = new GString(); + do { + c = str->getChar(); + } while (c != EOF && isspace(c)); + 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(); + } + } else { + while (1) { + s->append((char)c); + c = str->lookChar(); + if (c == EOF || !isalnum(c)) { + break; + } + str->getChar(); + } + } + return s; +} + +void PostScriptFunction::resizeCode(int newSize) { + if (newSize >= codeSize) { + codeSize += 64; + code = (PSObject *)grealloc(code, codeSize * sizeof(PSObject)); + } +} + +void PostScriptFunction::exec(PSStack *stack, int codePtr) { + int i1, i2; + double r1, r2; + GBool b1, b2; + + while (1) { + switch (code[codePtr].type) { + case psInt: + stack->pushInt(code[codePtr++].intg); + break; + case psReal: + stack->pushReal(code[codePtr++].real); + break; + case psOperator: + switch (code[codePtr++].op) { + case psOpAbs: + if (stack->topIsInt()) { + stack->pushInt(abs(stack->popInt())); + } else { + stack->pushReal(fabs(stack->popNum())); + } + break; + case psOpAdd: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 + i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushReal(r1 + r2); + } + break; + case psOpAnd: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 & i2); + } else { + b2 = stack->popBool(); + b1 = stack->popBool(); + stack->pushReal(b1 && b2); + } + break; + case psOpAtan: + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushReal(atan2(r1, r2)); + break; + case psOpBitshift: + i2 = stack->popInt(); + i1 = stack->popInt(); + if (i2 > 0) { + stack->pushInt(i1 << i2); + } else if (i2 < 0) { + stack->pushInt((int)((Guint)i1 >> i2)); + } else { + stack->pushInt(i1); + } + break; + case psOpCeiling: + if (!stack->topIsInt()) { + stack->pushReal(ceil(stack->popNum())); + } + break; + case psOpCopy: + stack->copy(stack->popInt()); + break; + case psOpCos: + stack->pushReal(cos(stack->popNum())); + break; + case psOpCvi: + if (!stack->topIsInt()) { + stack->pushInt((int)stack->popNum()); + } + break; + case psOpCvr: + if (!stack->topIsReal()) { + stack->pushReal(stack->popNum()); + } + break; + case psOpDiv: + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushReal(r1 / r2); + break; + case psOpDup: + stack->copy(1); + break; + case psOpEq: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 == i2); + } else if (stack->topTwoAreNums()) { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 == r2); + } else { + b2 = stack->popBool(); + b1 = stack->popBool(); + stack->pushBool(b1 == b2); + } + break; + case psOpExch: + stack->roll(2, 1); + break; + case psOpExp: + r2 = stack->popInt(); + r1 = stack->popInt(); + stack->pushReal(pow(r1, r2)); + break; + case psOpFalse: + stack->pushBool(gFalse); + break; + case psOpFloor: + if (!stack->topIsInt()) { + stack->pushReal(floor(stack->popNum())); + } + break; + case psOpGe: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 >= i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 >= r2); + } + break; + case psOpGt: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 > i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 > r2); + } + break; + case psOpIdiv: + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 / i2); + break; + case psOpIndex: + stack->index(stack->popInt()); + break; + case psOpLe: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 <= i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 <= r2); + } + break; + case psOpLn: + stack->pushReal(log(stack->popNum())); + break; + case psOpLog: + stack->pushReal(log10(stack->popNum())); + break; + case psOpLt: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 < i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 < r2); + } + break; + case psOpMod: + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 % i2); + break; + case psOpMul: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + //~ should check for out-of-range, and push a real instead + stack->pushInt(i1 * i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushReal(r1 * r2); + } + break; + case psOpNe: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushBool(i1 != i2); + } else if (stack->topTwoAreNums()) { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushBool(r1 != r2); + } else { + b2 = stack->popBool(); + b1 = stack->popBool(); + stack->pushBool(b1 != b2); + } + break; + case psOpNeg: + if (stack->topIsInt()) { + stack->pushInt(-stack->popInt()); + } else { + stack->pushReal(-stack->popNum()); + } + break; + case psOpNot: + if (stack->topIsInt()) { + stack->pushInt(~stack->popInt()); + } else { + stack->pushReal(!stack->popBool()); + } + break; + case psOpOr: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 | i2); + } else { + b2 = stack->popBool(); + b1 = stack->popBool(); + stack->pushReal(b1 || b2); + } + break; + case psOpPop: + stack->pop(); + break; + case psOpRoll: + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->roll(i1, i2); + break; + case psOpRound: + if (!stack->topIsInt()) { + r1 = stack->popNum(); + stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5)); + } + break; + case psOpSin: + stack->pushReal(cos(stack->popNum())); + break; + case psOpSqrt: + stack->pushReal(sqrt(stack->popNum())); + break; + case psOpSub: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 - i2); + } else { + r2 = stack->popNum(); + r1 = stack->popNum(); + stack->pushReal(r1 - r2); + } + break; + case psOpTrue: + stack->pushBool(gTrue); + break; + case psOpTruncate: + if (!stack->topIsInt()) { + r1 = stack->popNum(); + stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1)); + } + break; + case psOpXor: + if (stack->topTwoAreInts()) { + i2 = stack->popInt(); + i1 = stack->popInt(); + stack->pushInt(i1 ^ i2); + } else { + b2 = stack->popBool(); + b1 = stack->popBool(); + stack->pushReal(b1 ^ b2); + } + break; + case psOpIf: + b1 = stack->popBool(); + if (b1) { + exec(stack, codePtr + 2); + } + codePtr = code[codePtr + 1].blk; + break; + case psOpIfelse: + b1 = stack->popBool(); + if (b1) { + exec(stack, codePtr + 2); + } else { + exec(stack, code[codePtr].blk); + } + codePtr = code[codePtr + 1].blk; + break; + case psOpReturn: + return; + } + break; + default: + error(-1, "Internal: bad object in PostScript function code"); + break; + } + } +}