You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1514 lines
32 KiB
1514 lines
32 KiB
//======================================================================== |
|
// |
|
// 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 "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<<m); ++j) { |
|
idx = e[j & 1][m - 1]; |
|
for (k = m - 2; k >= 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; |
|
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; |
|
} |
|
|
|
//----- C0 |
|
if (dict->lookup("C0", &obj1)->isArray()) { |
|
if (hasRange && obj1.arrayGetLength() != n) { |
|
error(-1, "Function's C0 array is wrong length"); |
|
goto err2; |
|
} |
|
n = obj1.arrayGetLength(); |
|
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(); |
|
} |
|
} else { |
|
if (hasRange && n != 1) { |
|
error(-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(-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(); |
|
} |
|
} else { |
|
if (n != 1) { |
|
error(-1, "Function's C1 array is wrong length"); |
|
goto err2; |
|
} |
|
c1[0] = 1; |
|
} |
|
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(); |
|
|
|
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=<A> | |
|
// +---------------------------------+ |
|
// | psBlock: ptr=<B> | |
|
// +---------------------------------+ |
|
// | if clause | |
|
// | ... | |
|
// | psOperator: psOpReturn | |
|
// +---------------------------------+ |
|
// <A> | else clause | |
|
// | ... | |
|
// | psOperator: psOpReturn | |
|
// +---------------------------------+ |
|
// <B> | ... | |
|
// |
|
// For 'if', pointer <A> 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; |
|
} |
|
delete tok; |
|
if (!(tok = getToken(str))) { |
|
error(-1, "Unexpected end of PostScript function stream"); |
|
return gFalse; |
|
} |
|
} else { |
|
elsePtr = -1; |
|
} |
|
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; |
|
} |
|
} |
|
}
|
|
|