0,0 → 1,1694 |
#include "fitz.h" |
#include "mupdf.h" |
|
enum |
{ |
MAXN = FZ_MAX_COLORS, |
MAXM = FZ_MAX_COLORS, |
}; |
|
typedef struct psobj_s psobj; |
|
enum |
{ |
SAMPLE = 0, |
EXPONENTIAL = 2, |
STITCHING = 3, |
POSTSCRIPT = 4 |
}; |
|
struct pdf_function_s |
{ |
int refs; |
int type; /* 0=sample 2=exponential 3=stitching 4=postscript */ |
int m; /* number of input values */ |
int n; /* number of output values */ |
float domain[MAXM][2]; /* even index : min value, odd index : max value */ |
float range[MAXN][2]; /* even index : min value, odd index : max value */ |
int has_range; |
|
union |
{ |
struct { |
unsigned short bps; |
int size[MAXM]; |
float encode[MAXM][2]; |
float decode[MAXN][2]; |
float *samples; |
} sa; |
|
struct { |
float n; |
float c0[MAXN]; |
float c1[MAXN]; |
} e; |
|
struct { |
int k; |
pdf_function **funcs; /* k */ |
float *bounds; /* k - 1 */ |
float *encode; /* k * 2 */ |
} st; |
|
struct { |
psobj *code; |
int cap; |
} p; |
} u; |
}; |
|
#define RADIAN 57.2957795 |
|
static inline float lerp(float x, float xmin, float xmax, float ymin, float ymax) |
{ |
if (xmin == xmax) |
return ymin; |
if (ymin == ymax) |
return ymin; |
return ymin + (x - xmin) * (ymax - ymin) / (xmax - xmin); |
} |
|
/* |
* PostScript calculator |
*/ |
|
enum { PS_BOOL, PS_INT, PS_REAL, PS_OPERATOR, PS_BLOCK }; |
|
enum |
{ |
PS_OP_ABS, PS_OP_ADD, PS_OP_AND, PS_OP_ATAN, PS_OP_BITSHIFT, |
PS_OP_CEILING, PS_OP_COPY, PS_OP_COS, PS_OP_CVI, PS_OP_CVR, |
PS_OP_DIV, PS_OP_DUP, PS_OP_EQ, PS_OP_EXCH, PS_OP_EXP, |
PS_OP_FALSE, PS_OP_FLOOR, PS_OP_GE, PS_OP_GT, PS_OP_IDIV, |
PS_OP_INDEX, PS_OP_LE, PS_OP_LN, PS_OP_LOG, PS_OP_LT, PS_OP_MOD, |
PS_OP_MUL, PS_OP_NE, PS_OP_NEG, PS_OP_NOT, PS_OP_OR, PS_OP_POP, |
PS_OP_ROLL, PS_OP_ROUND, PS_OP_SIN, PS_OP_SQRT, PS_OP_SUB, |
PS_OP_TRUE, PS_OP_TRUNCATE, PS_OP_XOR, PS_OP_IF, PS_OP_IFELSE, |
PS_OP_RETURN |
}; |
|
static char *ps_op_names[] = |
{ |
"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", "if", "ifelse", "return" |
}; |
|
struct psobj_s |
{ |
int type; |
union |
{ |
int b; /* boolean (stack only) */ |
int i; /* integer (stack and code) */ |
float f; /* real (stack and code) */ |
int op; /* operator (code only) */ |
int block; /* if/ifelse block pointer (code only) */ |
} u; |
}; |
|
typedef struct ps_stack_s ps_stack; |
|
struct ps_stack_s |
{ |
psobj stack[100]; |
int sp; |
}; |
|
void |
pdf_debug_ps_stack(ps_stack *st) |
{ |
int i; |
|
printf("stack: "); |
|
for (i = 0; i < st->sp; i++) |
{ |
switch (st->stack[i].type) |
{ |
case PS_BOOL: |
if (st->stack[i].u.b) |
printf("true "); |
else |
printf("false "); |
break; |
|
case PS_INT: |
printf("%d ", st->stack[i].u.i); |
break; |
|
case PS_REAL: |
printf("%g ", st->stack[i].u.f); |
break; |
} |
} |
printf("\n"); |
|
} |
|
static void |
ps_init_stack(ps_stack *st) |
{ |
memset(st->stack, 0, sizeof(st->stack)); |
st->sp = 0; |
} |
|
static inline int ps_overflow(ps_stack *st, int n) |
{ |
return n < 0 || st->sp + n >= nelem(st->stack); |
} |
|
static inline int ps_underflow(ps_stack *st, int n) |
{ |
return n < 0 || st->sp - n < 0; |
} |
|
static inline int ps_is_type(ps_stack *st, int t) |
{ |
return !ps_underflow(st, 1) && st->stack[st->sp - 1].type == t; |
} |
|
static inline int ps_is_type2(ps_stack *st, int t) |
{ |
return !ps_underflow(st, 2) && st->stack[st->sp - 1].type == t && st->stack[st->sp - 2].type == t; |
} |
|
static void |
ps_push_bool(ps_stack *st, int b) |
{ |
if (!ps_overflow(st, 1)) |
{ |
st->stack[st->sp].type = PS_BOOL; |
st->stack[st->sp].u.b = b; |
st->sp++; |
} |
} |
|
static void |
ps_push_int(ps_stack *st, int n) |
{ |
if (!ps_overflow(st, 1)) |
{ |
st->stack[st->sp].type = PS_INT; |
st->stack[st->sp].u.i = n; |
st->sp++; |
} |
} |
|
static void |
ps_push_real(ps_stack *st, float n) |
{ |
if (!ps_overflow(st, 1)) |
{ |
st->stack[st->sp].type = PS_REAL; |
st->stack[st->sp].u.f = n; |
st->sp++; |
} |
} |
|
static int |
ps_pop_bool(ps_stack *st) |
{ |
if (!ps_underflow(st, 1)) |
{ |
if (ps_is_type(st, PS_BOOL)) |
return st->stack[--st->sp].u.b; |
} |
return 0; |
} |
|
static int |
ps_pop_int(ps_stack *st) |
{ |
if (!ps_underflow(st, 1)) |
{ |
if (ps_is_type(st, PS_INT)) |
return st->stack[--st->sp].u.i; |
if (ps_is_type(st, PS_REAL)) |
return st->stack[--st->sp].u.f; |
} |
return 0; |
} |
|
static float |
ps_pop_real(ps_stack *st) |
{ |
if (!ps_underflow(st, 1)) |
{ |
if (ps_is_type(st, PS_INT)) |
return st->stack[--st->sp].u.i; |
if (ps_is_type(st, PS_REAL)) |
return st->stack[--st->sp].u.f; |
} |
return 0; |
} |
|
static void |
ps_copy(ps_stack *st, int n) |
{ |
if (!ps_underflow(st, n) && !ps_overflow(st, n)) |
{ |
memcpy(st->stack + st->sp, st->stack + st->sp - n, n * sizeof(psobj)); |
st->sp += n; |
} |
} |
|
static void |
ps_roll(ps_stack *st, int n, int j) |
{ |
psobj tmp; |
int i; |
|
if (ps_underflow(st, n) || j == 0 || n == 0) |
return; |
|
if (j >= 0) |
{ |
j %= n; |
} |
else |
{ |
j = -j % n; |
if (j != 0) |
j = n - j; |
} |
|
for (i = 0; i < j; i++) |
{ |
tmp = st->stack[st->sp - 1]; |
memmove(st->stack + st->sp - n + 1, st->stack + st->sp - n, n * sizeof(psobj)); |
st->stack[st->sp - n] = tmp; |
} |
} |
|
static void |
ps_index(ps_stack *st, int n) |
{ |
if (!ps_overflow(st, 1) && !ps_underflow(st, n)) |
{ |
st->stack[st->sp] = st->stack[st->sp - n - 1]; |
st->sp++; |
} |
} |
|
static void |
ps_run(psobj *code, ps_stack *st, int pc) |
{ |
int i1, i2; |
float r1, r2; |
int b1, b2; |
|
while (1) |
{ |
switch (code[pc].type) |
{ |
case PS_INT: |
ps_push_int(st, code[pc++].u.i); |
break; |
|
case PS_REAL: |
ps_push_real(st, code[pc++].u.f); |
break; |
|
case PS_OPERATOR: |
switch (code[pc++].u.op) |
{ |
case PS_OP_ABS: |
if (ps_is_type(st, PS_INT)) |
ps_push_int(st, abs(ps_pop_int(st))); |
else |
ps_push_real(st, fabsf(ps_pop_real(st))); |
break; |
|
case PS_OP_ADD: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 + i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_real(st, r1 + r2); |
} |
break; |
|
case PS_OP_AND: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 & i2); |
} |
else { |
b2 = ps_pop_bool(st); |
b1 = ps_pop_bool(st); |
ps_push_bool(st, b1 && b2); |
} |
break; |
|
case PS_OP_ATAN: |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
r1 = atan2f(r1, r2) * RADIAN; |
if (r1 < 0) |
r1 += 360; |
ps_push_real(st, r1); |
break; |
|
case PS_OP_BITSHIFT: |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
if (i2 > 0) |
ps_push_int(st, i1 << i2); |
else if (i2 < 0) |
ps_push_int(st, (int)((unsigned int)i1 >> i2)); |
else |
ps_push_int(st, i1); |
break; |
|
case PS_OP_CEILING: |
r1 = ps_pop_real(st); |
ps_push_real(st, ceilf(r1)); |
break; |
|
case PS_OP_COPY: |
ps_copy(st, ps_pop_int(st)); |
break; |
|
case PS_OP_COS: |
r1 = ps_pop_real(st); |
ps_push_real(st, cosf(r1/RADIAN)); |
break; |
|
case PS_OP_CVI: |
ps_push_int(st, ps_pop_int(st)); |
break; |
|
case PS_OP_CVR: |
ps_push_real(st, ps_pop_real(st)); |
break; |
|
case PS_OP_DIV: |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_real(st, r1 / r2); |
break; |
|
case PS_OP_DUP: |
ps_copy(st, 1); |
break; |
|
case PS_OP_EQ: |
if (ps_is_type2(st, PS_BOOL)) { |
b2 = ps_pop_bool(st); |
b1 = ps_pop_bool(st); |
ps_push_bool(st, b1 == b2); |
} |
else if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 == i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 == r2); |
} |
break; |
|
case PS_OP_EXCH: |
ps_roll(st, 2, 1); |
break; |
|
case PS_OP_EXP: |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_real(st, powf(r1, r2)); |
break; |
|
case PS_OP_FALSE: |
ps_push_bool(st, 0); |
break; |
|
case PS_OP_FLOOR: |
r1 = ps_pop_real(st); |
ps_push_real(st, floorf(r1)); |
break; |
|
case PS_OP_GE: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 >= i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 >= r2); |
} |
break; |
|
case PS_OP_GT: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 > i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 > r2); |
} |
break; |
|
case PS_OP_IDIV: |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 / i2); |
break; |
|
case PS_OP_INDEX: |
ps_index(st, ps_pop_int(st)); |
break; |
|
case PS_OP_LE: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 <= i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 <= r2); |
} |
break; |
|
case PS_OP_LN: |
r1 = ps_pop_real(st); |
ps_push_real(st, logf(r1)); |
break; |
|
case PS_OP_LOG: |
r1 = ps_pop_real(st); |
ps_push_real(st, log10f(r1)); |
break; |
|
case PS_OP_LT: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 < i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 < r2); |
} |
break; |
|
case PS_OP_MOD: |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 % i2); |
break; |
|
case PS_OP_MUL: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 * i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_real(st, r1 * r2); |
} |
break; |
|
case PS_OP_NE: |
if (ps_is_type2(st, PS_BOOL)) { |
b2 = ps_pop_bool(st); |
b1 = ps_pop_bool(st); |
ps_push_bool(st, b1 != b2); |
} |
else if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_bool(st, i1 != i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_bool(st, r1 != r2); |
} |
break; |
|
case PS_OP_NEG: |
if (ps_is_type(st, PS_INT)) |
ps_push_int(st, -ps_pop_int(st)); |
else |
ps_push_real(st, -ps_pop_real(st)); |
break; |
|
case PS_OP_NOT: |
if (ps_is_type(st, PS_BOOL)) |
ps_push_bool(st, !ps_pop_bool(st)); |
else |
ps_push_int(st, ~ps_pop_int(st)); |
break; |
|
case PS_OP_OR: |
if (ps_is_type2(st, PS_BOOL)) { |
b2 = ps_pop_bool(st); |
b1 = ps_pop_bool(st); |
ps_push_bool(st, b1 || b2); |
} |
else { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 | i2); |
} |
break; |
|
case PS_OP_POP: |
if (!ps_underflow(st, 1)) |
st->sp--; |
break; |
|
case PS_OP_ROLL: |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_roll(st, i1, i2); |
break; |
|
case PS_OP_ROUND: |
if (!ps_is_type(st, PS_INT)) { |
r1 = ps_pop_real(st); |
ps_push_real(st, (r1 >= 0) ? floorf(r1 + 0.5f) : ceilf(r1 - 0.5f)); |
} |
break; |
|
case PS_OP_SIN: |
r1 = ps_pop_real(st); |
ps_push_real(st, sinf(r1/RADIAN)); |
break; |
|
case PS_OP_SQRT: |
r1 = ps_pop_real(st); |
ps_push_real(st, sqrtf(r1)); |
break; |
|
case PS_OP_SUB: |
if (ps_is_type2(st, PS_INT)) { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 - i2); |
} |
else { |
r2 = ps_pop_real(st); |
r1 = ps_pop_real(st); |
ps_push_real(st, r1 - r2); |
} |
break; |
|
case PS_OP_TRUE: |
ps_push_bool(st, 1); |
break; |
|
case PS_OP_TRUNCATE: |
if (!ps_is_type(st, PS_INT)) { |
r1 = ps_pop_real(st); |
ps_push_real(st, (r1 >= 0) ? floorf(r1) : ceilf(r1)); |
} |
break; |
|
case PS_OP_XOR: |
if (ps_is_type2(st, PS_BOOL)) { |
b2 = ps_pop_bool(st); |
b1 = ps_pop_bool(st); |
ps_push_bool(st, b1 ^ b2); |
} |
else { |
i2 = ps_pop_int(st); |
i1 = ps_pop_int(st); |
ps_push_int(st, i1 ^ i2); |
} |
break; |
|
case PS_OP_IF: |
b1 = ps_pop_bool(st); |
if (b1) |
ps_run(code, st, code[pc + 1].u.block); |
pc = code[pc + 2].u.block; |
break; |
|
case PS_OP_IFELSE: |
b1 = ps_pop_bool(st); |
if (b1) |
ps_run(code, st, code[pc + 1].u.block); |
else |
ps_run(code, st, code[pc + 0].u.block); |
pc = code[pc + 2].u.block; |
break; |
|
case PS_OP_RETURN: |
return; |
|
default: |
fz_warn("foreign operator in calculator function"); |
return; |
} |
break; |
|
default: |
fz_warn("foreign object in calculator function"); |
return; |
} |
} |
} |
|
static void |
resize_code(pdf_function *func, int newsize) |
{ |
if (newsize >= func->u.p.cap) |
{ |
func->u.p.cap = func->u.p.cap + 64; |
func->u.p.code = fz_realloc(func->u.p.code, func->u.p.cap, sizeof(psobj)); |
} |
} |
|
static fz_error |
parse_code(pdf_function *func, fz_stream *stream, int *codeptr) |
{ |
fz_error error; |
char buf[64]; |
int len; |
int tok; |
int opptr, elseptr, ifptr; |
int a, b, mid, cmp; |
|
memset(buf, 0, sizeof(buf)); |
|
while (1) |
{ |
error = pdf_lex(&tok, stream, buf, sizeof buf, &len); |
if (error) |
return fz_rethrow(error, "calculator function lexical error"); |
|
switch(tok) |
{ |
case PDF_TOK_EOF: |
return fz_throw("truncated calculator function"); |
|
case PDF_TOK_INT: |
resize_code(func, *codeptr); |
func->u.p.code[*codeptr].type = PS_INT; |
func->u.p.code[*codeptr].u.i = atoi(buf); |
++*codeptr; |
break; |
|
case PDF_TOK_REAL: |
resize_code(func, *codeptr); |
func->u.p.code[*codeptr].type = PS_REAL; |
func->u.p.code[*codeptr].u.f = fz_atof(buf); |
++*codeptr; |
break; |
|
case PDF_TOK_OPEN_BRACE: |
opptr = *codeptr; |
*codeptr += 4; |
|
resize_code(func, *codeptr); |
|
ifptr = *codeptr; |
error = parse_code(func, stream, codeptr); |
if (error) |
return fz_rethrow(error, "error in 'if' branch"); |
|
error = pdf_lex(&tok, stream, buf, sizeof buf, &len); |
if (error) |
return fz_rethrow(error, "calculator function syntax error"); |
|
if (tok == PDF_TOK_OPEN_BRACE) |
{ |
elseptr = *codeptr; |
error = parse_code(func, stream, codeptr); |
if (error) |
return fz_rethrow(error, "error in 'else' branch"); |
|
error = pdf_lex(&tok, stream, buf, sizeof buf, &len); |
if (error) |
return fz_rethrow(error, "calculator function syntax error"); |
} |
else |
{ |
elseptr = -1; |
} |
|
if (tok == PDF_TOK_KEYWORD) |
{ |
if (!strcmp(buf, "if")) |
{ |
if (elseptr >= 0) |
return fz_throw("too many branches for 'if'"); |
func->u.p.code[opptr].type = PS_OPERATOR; |
func->u.p.code[opptr].u.op = PS_OP_IF; |
func->u.p.code[opptr+2].type = PS_BLOCK; |
func->u.p.code[opptr+2].u.block = ifptr; |
func->u.p.code[opptr+3].type = PS_BLOCK; |
func->u.p.code[opptr+3].u.block = *codeptr; |
} |
else if (!strcmp(buf, "ifelse")) |
{ |
if (elseptr < 0) |
return fz_throw("not enough branches for 'ifelse'"); |
func->u.p.code[opptr].type = PS_OPERATOR; |
func->u.p.code[opptr].u.op = PS_OP_IFELSE; |
func->u.p.code[opptr+1].type = PS_BLOCK; |
func->u.p.code[opptr+1].u.block = elseptr; |
func->u.p.code[opptr+2].type = PS_BLOCK; |
func->u.p.code[opptr+2].u.block = ifptr; |
func->u.p.code[opptr+3].type = PS_BLOCK; |
func->u.p.code[opptr+3].u.block = *codeptr; |
} |
else |
{ |
return fz_throw("unknown keyword in 'if-else' context: '%s'", buf); |
} |
} |
else |
{ |
return fz_throw("missing keyword in 'if-else' context"); |
} |
break; |
|
case PDF_TOK_CLOSE_BRACE: |
resize_code(func, *codeptr); |
func->u.p.code[*codeptr].type = PS_OPERATOR; |
func->u.p.code[*codeptr].u.op = PS_OP_RETURN; |
++*codeptr; |
return fz_okay; |
|
case PDF_TOK_KEYWORD: |
cmp = -1; |
a = -1; |
b = nelem(ps_op_names); |
while (b - a > 1) |
{ |
mid = (a + b) / 2; |
cmp = strcmp(buf, ps_op_names[mid]); |
if (cmp > 0) |
a = mid; |
else if (cmp < 0) |
b = mid; |
else |
a = b = mid; |
} |
if (cmp != 0) |
return fz_throw("unknown operator: '%s'", buf); |
|
resize_code(func, *codeptr); |
func->u.p.code[*codeptr].type = PS_OPERATOR; |
func->u.p.code[*codeptr].u.op = a; |
++*codeptr; |
break; |
|
default: |
return fz_throw("calculator function syntax error"); |
} |
} |
} |
|
static fz_error |
load_postscript_func(pdf_function *func, pdf_xref *xref, fz_obj *dict, int num, int gen) |
{ |
fz_error error; |
fz_stream *stream; |
int codeptr; |
char buf[64]; |
int tok; |
int len; |
|
error = pdf_open_stream(&stream, xref, num, gen); |
if (error) |
return fz_rethrow(error, "cannot open calculator function stream"); |
|
error = pdf_lex(&tok, stream, buf, sizeof buf, &len); |
if (error) |
{ |
fz_close(stream); |
return fz_rethrow(error, "stream is not a calculator function"); |
} |
|
if (tok != PDF_TOK_OPEN_BRACE) |
{ |
fz_close(stream); |
return fz_throw("stream is not a calculator function"); |
} |
|
func->u.p.code = NULL; |
func->u.p.cap = 0; |
|
codeptr = 0; |
error = parse_code(func, stream, &codeptr); |
if (error) |
{ |
fz_close(stream); |
return fz_rethrow(error, "cannot parse calculator function (%d %d R)", num, gen); |
} |
|
fz_close(stream); |
return fz_okay; |
} |
|
static void |
eval_postscript_func(pdf_function *func, float *in, float *out) |
{ |
ps_stack st; |
float x; |
int i; |
|
ps_init_stack(&st); |
|
for (i = 0; i < func->m; i++) |
{ |
x = CLAMP(in[i], func->domain[i][0], func->domain[i][1]); |
ps_push_real(&st, x); |
} |
|
ps_run(func->u.p.code, &st, 0); |
|
for (i = func->n - 1; i >= 0; i--) |
{ |
x = ps_pop_real(&st); |
out[i] = CLAMP(x, func->range[i][0], func->range[i][1]); |
} |
} |
|
/* |
* Sample function |
*/ |
|
static fz_error |
load_sample_func(pdf_function *func, pdf_xref *xref, fz_obj *dict, int num, int gen) |
{ |
fz_error error; |
fz_stream *stream; |
fz_obj *obj; |
int samplecount; |
int bps; |
int i; |
|
func->u.sa.samples = NULL; |
|
obj = fz_dict_gets(dict, "Size"); |
if (!fz_is_array(obj) || fz_array_len(obj) != func->m) |
return fz_throw("malformed /Size"); |
for (i = 0; i < func->m; i++) |
func->u.sa.size[i] = fz_to_int(fz_array_get(obj, i)); |
|
obj = fz_dict_gets(dict, "BitsPerSample"); |
if (!fz_is_int(obj)) |
return fz_throw("malformed /BitsPerSample"); |
func->u.sa.bps = bps = fz_to_int(obj); |
|
obj = fz_dict_gets(dict, "Encode"); |
if (fz_is_array(obj)) |
{ |
if (fz_array_len(obj) != func->m * 2) |
return fz_throw("malformed /Encode"); |
for (i = 0; i < func->m; i++) |
{ |
func->u.sa.encode[i][0] = fz_to_real(fz_array_get(obj, i*2+0)); |
func->u.sa.encode[i][1] = fz_to_real(fz_array_get(obj, i*2+1)); |
} |
} |
else |
{ |
for (i = 0; i < func->m; i++) |
{ |
func->u.sa.encode[i][0] = 0; |
func->u.sa.encode[i][1] = func->u.sa.size[i] - 1; |
} |
} |
|
obj = fz_dict_gets(dict, "Decode"); |
if (fz_is_array(obj)) |
{ |
if (fz_array_len(obj) != func->n * 2) |
return fz_throw("malformed /Decode"); |
for (i = 0; i < func->n; i++) |
{ |
func->u.sa.decode[i][0] = fz_to_real(fz_array_get(obj, i*2+0)); |
func->u.sa.decode[i][1] = fz_to_real(fz_array_get(obj, i*2+1)); |
} |
} |
else |
{ |
for (i = 0; i < func->n; i++) |
{ |
func->u.sa.decode[i][0] = func->range[i][0]; |
func->u.sa.decode[i][1] = func->range[i][1]; |
} |
} |
|
for (i = 0, samplecount = func->n; i < func->m; i++) |
samplecount *= func->u.sa.size[i]; |
|
func->u.sa.samples = fz_calloc(samplecount, sizeof(float)); |
|
error = pdf_open_stream(&stream, xref, num, gen); |
if (error) |
return fz_rethrow(error, "cannot open samples stream (%d %d R)", num, gen); |
|
/* read samples */ |
for (i = 0; i < samplecount; i++) |
{ |
unsigned int x; |
float s; |
|
if (fz_is_eof_bits(stream)) |
{ |
fz_close(stream); |
return fz_throw("truncated sample stream"); |
} |
|
switch (bps) |
{ |
case 1: s = fz_read_bits(stream, 1); break; |
case 2: s = fz_read_bits(stream, 2) / 3.0f; break; |
case 4: s = fz_read_bits(stream, 4) / 15.0f; break; |
case 8: s = fz_read_byte(stream) / 255.0f; break; |
case 12: s = fz_read_bits(stream, 12) / 4095.0f; break; |
case 16: |
x = fz_read_byte(stream) << 8; |
x |= fz_read_byte(stream); |
s = x / 65535.0f; |
break; |
case 24: |
x = fz_read_byte(stream) << 16; |
x |= fz_read_byte(stream) << 8; |
x |= fz_read_byte(stream); |
s = x / 16777215.0f; |
break; |
case 32: |
x = fz_read_byte(stream) << 24; |
x |= fz_read_byte(stream) << 16; |
x |= fz_read_byte(stream) << 8; |
x |= fz_read_byte(stream); |
s = x / 4294967295.0f; |
break; |
default: |
fz_close(stream); |
return fz_throw("sample stream bit depth %d unsupported", bps); |
} |
|
func->u.sa.samples[i] = s; |
} |
|
fz_close(stream); |
|
return fz_okay; |
} |
|
static float |
interpolate_sample(pdf_function *func, int *scale, int *e0, int *e1, float *efrac, int dim, int idx) |
{ |
float a, b; |
int idx0, idx1; |
|
idx0 = e0[dim] * scale[dim] + idx; |
idx1 = e1[dim] * scale[dim] + idx; |
|
if (dim == 0) |
{ |
a = func->u.sa.samples[idx0]; |
b = func->u.sa.samples[idx1]; |
} |
else |
{ |
a = interpolate_sample(func, scale, e0, e1, efrac, dim - 1, idx0); |
b = interpolate_sample(func, scale, e0, e1, efrac, dim - 1, idx1); |
} |
|
return a + (b - a) * efrac[dim]; |
} |
|
static void |
eval_sample_func(pdf_function *func, float *in, float *out) |
{ |
int e0[MAXM], e1[MAXM], scale[MAXM]; |
float efrac[MAXM]; |
float x; |
int i; |
|
/* encode input coordinates */ |
for (i = 0; i < func->m; i++) |
{ |
x = CLAMP(in[i], func->domain[i][0], func->domain[i][1]); |
x = lerp(x, func->domain[i][0], func->domain[i][1], |
func->u.sa.encode[i][0], func->u.sa.encode[i][1]); |
x = CLAMP(x, 0, func->u.sa.size[i] - 1); |
e0[i] = floorf(x); |
e1[i] = ceilf(x); |
efrac[i] = x - floorf(x); |
} |
|
scale[0] = func->n; |
for (i = 1; i < func->m; i++) |
scale[i] = scale[i - 1] * func->u.sa.size[i]; |
|
for (i = 0; i < func->n; i++) |
{ |
if (func->m == 1) |
{ |
float a = func->u.sa.samples[e0[0] * func->n + i]; |
float b = func->u.sa.samples[e1[0] * func->n + i]; |
|
float ab = a + (b - a) * efrac[0]; |
|
out[i] = lerp(ab, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]); |
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]); |
} |
|
else if (func->m == 2) |
{ |
int s0 = func->n; |
int s1 = s0 * func->u.sa.size[0]; |
|
float a = func->u.sa.samples[e0[0] * s0 + e0[1] * s1 + i]; |
float b = func->u.sa.samples[e1[0] * s0 + e0[1] * s1 + i]; |
float c = func->u.sa.samples[e0[0] * s0 + e1[1] * s1 + i]; |
float d = func->u.sa.samples[e1[0] * s0 + e1[1] * s1 + i]; |
|
float ab = a + (b - a) * efrac[0]; |
float cd = c + (d - c) * efrac[0]; |
float abcd = ab + (cd - ab) * efrac[1]; |
|
out[i] = lerp(abcd, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]); |
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]); |
} |
|
else |
{ |
float x = interpolate_sample(func, scale, e0, e1, efrac, func->m - 1, i); |
out[i] = lerp(x, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]); |
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]); |
} |
} |
} |
|
/* |
* Exponential function |
*/ |
|
static fz_error |
load_exponential_func(pdf_function *func, fz_obj *dict) |
{ |
fz_obj *obj; |
int i; |
|
if (func->m != 1) |
return fz_throw("/Domain must be one dimension (%d)", func->m); |
|
obj = fz_dict_gets(dict, "N"); |
if (!fz_is_int(obj) && !fz_is_real(obj)) |
return fz_throw("malformed /N"); |
func->u.e.n = fz_to_real(obj); |
|
obj = fz_dict_gets(dict, "C0"); |
if (fz_is_array(obj)) |
{ |
func->n = fz_array_len(obj); |
if (func->n >= MAXN) |
return fz_throw("exponential function result array out of range"); |
for (i = 0; i < func->n; i++) |
func->u.e.c0[i] = fz_to_real(fz_array_get(obj, i)); |
} |
else |
{ |
func->n = 1; |
func->u.e.c0[0] = 0; |
} |
|
obj = fz_dict_gets(dict, "C1"); |
if (fz_is_array(obj)) |
{ |
if (fz_array_len(obj) != func->n) |
return fz_throw("/C1 must match /C0 length"); |
for (i = 0; i < func->n; i++) |
func->u.e.c1[i] = fz_to_real(fz_array_get(obj, i)); |
} |
else |
{ |
if (func->n != 1) |
return fz_throw("/C1 must match /C0 length"); |
func->u.e.c1[0] = 1; |
} |
|
return fz_okay; |
} |
|
static void |
eval_exponential_func(pdf_function *func, float in, float *out) |
{ |
float x = in; |
float tmp; |
int i; |
|
x = CLAMP(x, func->domain[0][0], func->domain[0][1]); |
|
/* constraint */ |
if ((func->u.e.n != (int)func->u.e.n && x < 0) || (func->u.e.n < 0 && x == 0)) |
{ |
fz_warn("constraint error"); |
return; |
} |
|
tmp = powf(x, func->u.e.n); |
for (i = 0; i < func->n; i++) |
{ |
out[i] = func->u.e.c0[i] + tmp * (func->u.e.c1[i] - func->u.e.c0[i]); |
if (func->has_range) |
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]); |
} |
} |
|
/* |
* Stitching function |
*/ |
|
static fz_error |
load_stitching_func(pdf_function *func, pdf_xref *xref, fz_obj *dict) |
{ |
pdf_function **funcs; |
fz_error error; |
fz_obj *obj; |
fz_obj *sub; |
fz_obj *num; |
int k; |
int i; |
|
func->u.st.k = 0; |
|
if (func->m != 1) |
return fz_throw("/Domain must be one dimension (%d)", func->m); |
|
obj = fz_dict_gets(dict, "Functions"); |
if (!fz_is_array(obj)) |
return fz_throw("stitching function has no input functions"); |
{ |
k = fz_array_len(obj); |
|
func->u.st.funcs = fz_calloc(k, sizeof(pdf_function*)); |
func->u.st.bounds = fz_calloc(k - 1, sizeof(float)); |
func->u.st.encode = fz_calloc(k * 2, sizeof(float)); |
funcs = func->u.st.funcs; |
|
for (i = 0; i < k; i++) |
{ |
sub = fz_array_get(obj, i); |
error = pdf_load_function(&funcs[i], xref, sub); |
if (error) |
return fz_rethrow(error, "cannot load sub function %d (%d %d R)", i, fz_to_num(sub), fz_to_gen(sub)); |
if (funcs[i]->m != 1 || funcs[i]->n != funcs[0]->n) |
return fz_throw("sub function %d /Domain or /Range mismatch", i); |
func->u.st.k ++; |
} |
|
if (!func->n) |
func->n = funcs[0]->n; |
else if (func->n != funcs[0]->n) |
return fz_throw("sub function /Domain or /Range mismatch"); |
} |
|
obj = fz_dict_gets(dict, "Bounds"); |
if (!fz_is_array(obj)) |
return fz_throw("stitching function has no bounds"); |
{ |
if (!fz_is_array(obj) || fz_array_len(obj) != k - 1) |
return fz_throw("malformed /Bounds (not array or wrong length)"); |
|
for (i = 0; i < k-1; i++) |
{ |
num = fz_array_get(obj, i); |
if (!fz_is_int(num) && !fz_is_real(num)) |
return fz_throw("malformed /Bounds (item not real)"); |
func->u.st.bounds[i] = fz_to_real(num); |
if (i && func->u.st.bounds[i-1] > func->u.st.bounds[i]) |
return fz_throw("malformed /Bounds (item not monotonic)"); |
} |
|
if (k != 1 && (func->domain[0][0] > func->u.st.bounds[0] || |
func->domain[0][1] < func->u.st.bounds[k-2])) |
fz_warn("malformed shading function bounds (domain mismatch), proceeding anyway."); |
} |
|
obj = fz_dict_gets(dict, "Encode"); |
if (!fz_is_array(obj)) |
return fz_throw("stitching function is missing encoding"); |
{ |
if (!fz_is_array(obj) || fz_array_len(obj) != k * 2) |
return fz_throw("malformed /Encode"); |
for (i = 0; i < k; i++) |
{ |
func->u.st.encode[i*2+0] = fz_to_real(fz_array_get(obj, i*2+0)); |
func->u.st.encode[i*2+1] = fz_to_real(fz_array_get(obj, i*2+1)); |
} |
} |
|
return fz_okay; |
} |
|
static void |
eval_stitching_func(pdf_function *func, float in, float *out) |
{ |
float low, high; |
int k = func->u.st.k; |
float *bounds = func->u.st.bounds; |
int i; |
|
in = CLAMP(in, func->domain[0][0], func->domain[0][1]); |
|
for (i = 0; i < k - 1; i++) |
{ |
if (in < bounds[i]) |
break; |
} |
|
if (i == 0 && k == 1) |
{ |
low = func->domain[0][0]; |
high = func->domain[0][1]; |
} |
else if (i == 0) |
{ |
low = func->domain[0][0]; |
high = bounds[0]; |
} |
else if (i == k - 1) |
{ |
low = bounds[k-2]; |
high = func->domain[0][1]; |
} |
else |
{ |
low = bounds[i-1]; |
high = bounds[i]; |
} |
|
in = lerp(in, low, high, func->u.st.encode[i*2+0], func->u.st.encode[i*2+1]); |
|
pdf_eval_function(func->u.st.funcs[i], &in, 1, out, func->n); |
} |
|
/* |
* Common |
*/ |
|
pdf_function * |
pdf_keep_function(pdf_function *func) |
{ |
func->refs ++; |
return func; |
} |
|
void |
pdf_drop_function(pdf_function *func) |
{ |
int i; |
if (--func->refs == 0) |
{ |
switch(func->type) |
{ |
case SAMPLE: |
fz_free(func->u.sa.samples); |
break; |
case EXPONENTIAL: |
break; |
case STITCHING: |
for (i = 0; i < func->u.st.k; i++) |
pdf_drop_function(func->u.st.funcs[i]); |
fz_free(func->u.st.funcs); |
fz_free(func->u.st.bounds); |
fz_free(func->u.st.encode); |
break; |
case POSTSCRIPT: |
fz_free(func->u.p.code); |
break; |
} |
fz_free(func); |
} |
} |
|
fz_error |
pdf_load_function(pdf_function **funcp, pdf_xref *xref, fz_obj *dict) |
{ |
fz_error error; |
pdf_function *func; |
fz_obj *obj; |
int i; |
|
if ((*funcp = pdf_find_item(xref->store, pdf_drop_function, dict))) |
{ |
pdf_keep_function(*funcp); |
return fz_okay; |
} |
|
func = fz_malloc(sizeof(pdf_function)); |
memset(func, 0, sizeof(pdf_function)); |
func->refs = 1; |
|
obj = fz_dict_gets(dict, "FunctionType"); |
func->type = fz_to_int(obj); |
|
/* required for all */ |
obj = fz_dict_gets(dict, "Domain"); |
func->m = fz_array_len(obj) / 2; |
for (i = 0; i < func->m; i++) |
{ |
func->domain[i][0] = fz_to_real(fz_array_get(obj, i * 2 + 0)); |
func->domain[i][1] = fz_to_real(fz_array_get(obj, i * 2 + 1)); |
} |
|
/* required for type0 and type4, optional otherwise */ |
obj = fz_dict_gets(dict, "Range"); |
if (fz_is_array(obj)) |
{ |
func->has_range = 1; |
func->n = fz_array_len(obj) / 2; |
for (i = 0; i < func->n; i++) |
{ |
func->range[i][0] = fz_to_real(fz_array_get(obj, i * 2 + 0)); |
func->range[i][1] = fz_to_real(fz_array_get(obj, i * 2 + 1)); |
} |
} |
else |
{ |
func->has_range = 0; |
func->n = 0; |
} |
|
if (func->m >= MAXM || func->n >= MAXN) |
{ |
fz_free(func); |
return fz_throw("assert: /Domain or /Range too big"); |
} |
|
switch(func->type) |
{ |
case SAMPLE: |
error = load_sample_func(func, xref, dict, fz_to_num(dict), fz_to_gen(dict)); |
if (error) |
{ |
pdf_drop_function(func); |
return fz_rethrow(error, "cannot load sampled function (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); |
} |
break; |
|
case EXPONENTIAL: |
error = load_exponential_func(func, dict); |
if (error) |
{ |
pdf_drop_function(func); |
return fz_rethrow(error, "cannot load exponential function (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); |
} |
break; |
|
case STITCHING: |
error = load_stitching_func(func, xref, dict); |
if (error) |
{ |
pdf_drop_function(func); |
return fz_rethrow(error, "cannot load stitching function (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); |
} |
break; |
|
case POSTSCRIPT: |
error = load_postscript_func(func, xref, dict, fz_to_num(dict), fz_to_gen(dict)); |
if (error) |
{ |
pdf_drop_function(func); |
return fz_rethrow(error, "cannot load calculator function (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); |
} |
break; |
|
default: |
fz_free(func); |
return fz_throw("unknown function type (%d %d R)", fz_to_num(dict), fz_to_gen(dict)); |
} |
|
pdf_store_item(xref->store, pdf_keep_function, pdf_drop_function, dict, func); |
|
*funcp = func; |
return fz_okay; |
} |
|
void |
pdf_eval_function(pdf_function *func, float *in, int inlen, float *out, int outlen) |
{ |
memset(out, 0, sizeof(float) * outlen); |
|
if (inlen != func->m) |
{ |
fz_warn("tried to evaluate function with wrong number of inputs"); |
return; |
} |
if (func->n != outlen) |
{ |
fz_warn("tried to evaluate function with wrong number of outputs"); |
return; |
} |
|
switch(func->type) |
{ |
case SAMPLE: eval_sample_func(func, in, out); break; |
case EXPONENTIAL: eval_exponential_func(func, *in, out); break; |
case STITCHING: eval_stitching_func(func, *in, out); break; |
case POSTSCRIPT: eval_postscript_func(func, in, out); break; |
} |
} |
|
/* |
* Debugging prints |
*/ |
|
static void |
pdf_debug_indent(char *prefix, int level, char *suffix) |
{ |
int i; |
|
printf("%s", prefix); |
|
for (i = 0; i < level; i++) |
printf("\t"); |
|
printf("%s", suffix); |
} |
|
static void |
pdf_debug_ps_func_code(psobj *funccode, psobj *code, int level) |
{ |
int eof, wasop; |
|
pdf_debug_indent("", level, "{"); |
|
/* Print empty blocks as { }, instead of separating braces on different lines. */ |
if (code->type == PS_OPERATOR && code->u.op == PS_OP_RETURN) |
{ |
printf(" } "); |
return; |
} |
|
pdf_debug_indent("\n", ++level, ""); |
|
eof = 0; |
wasop = 0; |
while (!eof) |
{ |
switch (code->type) |
{ |
case PS_INT: |
if (wasop) |
pdf_debug_indent("\n", level, ""); |
|
printf("%d ", code->u.i); |
wasop = 0; |
code++; |
break; |
|
case PS_REAL: |
if (wasop) |
pdf_debug_indent("\n", level, ""); |
|
printf("%g ", code->u.f); |
wasop = 0; |
code++; |
break; |
|
case PS_OPERATOR: |
if (code->u.op == PS_OP_RETURN) |
{ |
printf("\n"); |
eof = 1; |
} |
else if (code->u.op == PS_OP_IF) |
{ |
printf("\n"); |
pdf_debug_ps_func_code(funccode, &funccode[(code + 2)->u.block], level); |
|
printf("%s", ps_op_names[code->u.op]); |
code = &funccode[(code + 3)->u.block]; |
if (code->type != PS_OPERATOR || code->u.op != PS_OP_RETURN) |
pdf_debug_indent("\n", level, ""); |
|
wasop = 0; |
} |
else if (code->u.op == PS_OP_IFELSE) |
{ |
printf("\n"); |
pdf_debug_ps_func_code(funccode, &funccode[(code + 2)->u.block], level); |
|
printf("\n"); |
pdf_debug_ps_func_code(funccode, &funccode[(code + 1)->u.block], level); |
|
printf("%s", ps_op_names[code->u.op]); |
code = &funccode[(code + 3)->u.block]; |
if (code->type != PS_OPERATOR || code->u.op != PS_OP_RETURN) |
pdf_debug_indent("\n", level, ""); |
|
wasop = 0; |
} |
else |
{ |
printf("%s ", ps_op_names[code->u.op]); |
code++; |
wasop = 1; |
} |
break; |
} |
} |
|
pdf_debug_indent("", --level, "} "); |
} |
|
static void |
pdf_debug_function_imp(pdf_function *func, int level) |
{ |
int i; |
|
pdf_debug_indent("", level, "function {\n"); |
|
pdf_debug_indent("", ++level, ""); |
switch (func->type) |
{ |
case SAMPLE: |
printf("sampled"); |
break; |
case EXPONENTIAL: |
printf("exponential"); |
break; |
case STITCHING: |
printf("stitching"); |
break; |
case POSTSCRIPT: |
printf("postscript"); |
break; |
} |
|
pdf_debug_indent("\n", level, ""); |
printf("%d input -> %d output\n", func->m, func->n); |
|
pdf_debug_indent("", level, "domain "); |
for (i = 0; i < func->m; i++) |
printf("%g %g ", func->domain[i][0], func->domain[i][1]); |
printf("\n"); |
|
if (func->has_range) |
{ |
pdf_debug_indent("", level, "range "); |
for (i = 0; i < func->n; i++) |
printf("%g %g ", func->range[i][0], func->range[i][1]); |
printf("\n"); |
} |
|
switch (func->type) |
{ |
case SAMPLE: |
pdf_debug_indent("", level, ""); |
printf("bps: %d\n", func->u.sa.bps); |
|
pdf_debug_indent("", level, ""); |
printf("size: [ "); |
for (i = 0; i < func->m; i++) |
printf("%d ", func->u.sa.size[i]); |
printf("]\n"); |
|
pdf_debug_indent("", level, ""); |
printf("encode: [ "); |
for (i = 0; i < func->m; i++) |
printf("%g %g ", func->u.sa.encode[i][0], func->u.sa.encode[i][1]); |
printf("]\n"); |
|
pdf_debug_indent("", level, ""); |
printf("decode: [ "); |
for (i = 0; i < func->m; i++) |
printf("%g %g ", func->u.sa.decode[i][0], func->u.sa.decode[i][1]); |
printf("]\n"); |
break; |
|
case EXPONENTIAL: |
pdf_debug_indent("", level, ""); |
printf("n: %g\n", func->u.e.n); |
|
pdf_debug_indent("", level, ""); |
printf("c0: [ "); |
for (i = 0; i < func->n; i++) |
printf("%g ", func->u.e.c0[i]); |
printf("]\n"); |
|
pdf_debug_indent("", level, ""); |
printf("c1: [ "); |
for (i = 0; i < func->n; i++) |
printf("%g ", func->u.e.c1[i]); |
printf("]\n"); |
break; |
|
case STITCHING: |
pdf_debug_indent("", level, ""); |
printf("%d functions\n", func->u.st.k); |
|
pdf_debug_indent("", level, ""); |
printf("bounds: [ "); |
for (i = 0; i < func->u.st.k - 1; i++) |
printf("%g ", func->u.st.bounds[i]); |
printf("]\n"); |
|
pdf_debug_indent("", level, ""); |
printf("encode: [ "); |
for (i = 0; i < func->u.st.k * 2; i++) |
printf("%g ", func->u.st.encode[i]); |
printf("]\n"); |
|
for (i = 0; i < func->u.st.k; i++) |
pdf_debug_function_imp(func->u.st.funcs[i], level); |
break; |
|
case POSTSCRIPT: |
pdf_debug_ps_func_code(func->u.p.code, func->u.p.code, level); |
printf("\n"); |
break; |
} |
|
pdf_debug_indent("", --level, "}\n"); |
} |
|
void |
pdf_debug_function(pdf_function *func) |
{ |
pdf_debug_function_imp(func, 0); |
} |