//======================================================================== // // 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 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")) { return new IdentityFunction(); } 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, 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; } 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 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 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) 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 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()) { 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), recursion + 1))) { goto err2; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { 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; } 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; } stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; ++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; } stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; ++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 (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; }