1 | //======================================================================== |
2 | // |
3 | // Function.cc |
4 | // |
5 | // Copyright 2001-2003 Glyph & Cog, LLC |
6 | // |
7 | //======================================================================== |
8 | |
9 | //======================================================================== |
10 | // |
11 | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | // |
13 | // All changes made under the Poppler project to this file are licensed |
14 | // under GPL version 2 or later |
15 | // |
16 | // Copyright (C) 2006, 2008-2010, 2013-2015, 2017-2020, 2022-2024 Albert Astals Cid <aacid@kde.org> |
17 | // Copyright (C) 2006 Jeff Muizelaar <jeff@infidigm.net> |
18 | // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> |
19 | // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com> |
20 | // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
21 | // Copyright (C) 2012 Adam Reichold <adamreichold@myopera.com> |
22 | // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it> |
23 | // |
24 | // To see a description of the changes please see the Changelog file that |
25 | // came with your tarball or type make ChangeLog if you are building from git |
26 | // |
27 | //======================================================================== |
28 | |
29 | #include <config.h> |
30 | |
31 | #include <cstdlib> |
32 | #include <cstring> |
33 | #include <cctype> |
34 | #include <cmath> |
35 | #include "goo/gmem.h" |
36 | #include "goo/gstrtod.h" |
37 | #include "Object.h" |
38 | #include "Dict.h" |
39 | #include "Stream.h" |
40 | #include "Error.h" |
41 | #include "Function.h" |
42 | |
43 | #ifndef M_PI |
44 | # define M_PI 3.14159265358979323846 |
45 | #endif |
46 | |
47 | //------------------------------------------------------------------------ |
48 | // Function |
49 | //------------------------------------------------------------------------ |
50 | |
51 | Function::Function() : domain {} { } |
52 | |
53 | Function::~Function() { } |
54 | |
55 | Function *Function::parse(Object *funcObj) |
56 | { |
57 | std::set<int> usedParents; |
58 | return parse(funcObj, usedParents: &usedParents); |
59 | } |
60 | |
61 | Function *Function::parse(Object *funcObj, std::set<int> *usedParents) |
62 | { |
63 | Function *func; |
64 | Dict *dict; |
65 | int funcType; |
66 | |
67 | if (funcObj->isStream()) { |
68 | dict = funcObj->streamGetDict(); |
69 | } else if (funcObj->isDict()) { |
70 | dict = funcObj->getDict(); |
71 | } else if (funcObj->isName(nameA: "Identity" )) { |
72 | return new IdentityFunction(); |
73 | } else { |
74 | error(category: errSyntaxError, pos: -1, msg: "Expected function dictionary or stream" ); |
75 | return nullptr; |
76 | } |
77 | |
78 | Object obj1 = dict->lookup(key: "FunctionType" ); |
79 | if (!obj1.isInt()) { |
80 | error(category: errSyntaxError, pos: -1, msg: "Function type is missing or wrong type" ); |
81 | return nullptr; |
82 | } |
83 | funcType = obj1.getInt(); |
84 | |
85 | if (funcType == 0) { |
86 | func = new SampledFunction(funcObj, dict); |
87 | } else if (funcType == 2) { |
88 | func = new ExponentialFunction(funcObj, dict); |
89 | } else if (funcType == 3) { |
90 | func = new StitchingFunction(funcObj, dict, usedParents); |
91 | } else if (funcType == 4) { |
92 | func = new PostScriptFunction(funcObj, dict); |
93 | } else { |
94 | error(category: errSyntaxError, pos: -1, msg: "Unimplemented function type ({0:d})" , funcType); |
95 | return nullptr; |
96 | } |
97 | if (!func->isOk()) { |
98 | delete func; |
99 | return nullptr; |
100 | } |
101 | |
102 | return func; |
103 | } |
104 | |
105 | Function::Function(const Function *func) |
106 | { |
107 | m = func->m; |
108 | n = func->n; |
109 | |
110 | memcpy(dest: domain, src: func->domain, funcMaxInputs * 2 * sizeof(double)); |
111 | memcpy(dest: range, src: func->range, funcMaxOutputs * 2 * sizeof(double)); |
112 | |
113 | hasRange = func->hasRange; |
114 | } |
115 | |
116 | bool Function::init(Dict *dict) |
117 | { |
118 | Object obj1; |
119 | int i; |
120 | |
121 | //----- Domain |
122 | obj1 = dict->lookup(key: "Domain" ); |
123 | if (!obj1.isArray()) { |
124 | error(category: errSyntaxError, pos: -1, msg: "Function is missing domain" ); |
125 | return false; |
126 | } |
127 | m = obj1.arrayGetLength() / 2; |
128 | if (m > funcMaxInputs) { |
129 | error(category: errSyntaxError, pos: -1, msg: "Functions with more than {0:d} inputs are unsupported" , funcMaxInputs); |
130 | return false; |
131 | } |
132 | for (i = 0; i < m; ++i) { |
133 | Object obj2 = obj1.arrayGet(i: 2 * i); |
134 | if (!obj2.isNum()) { |
135 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function domain array" ); |
136 | return false; |
137 | } |
138 | domain[i][0] = obj2.getNum(); |
139 | obj2 = obj1.arrayGet(i: 2 * i + 1); |
140 | if (!obj2.isNum()) { |
141 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function domain array" ); |
142 | return false; |
143 | } |
144 | domain[i][1] = obj2.getNum(); |
145 | } |
146 | |
147 | //----- Range |
148 | hasRange = false; |
149 | n = 0; |
150 | obj1 = dict->lookup(key: "Range" ); |
151 | if (obj1.isArray()) { |
152 | hasRange = true; |
153 | n = obj1.arrayGetLength() / 2; |
154 | if (n > funcMaxOutputs) { |
155 | error(category: errSyntaxError, pos: -1, msg: "Functions with more than {0:d} outputs are unsupported" , funcMaxOutputs); |
156 | return false; |
157 | } |
158 | for (i = 0; i < n; ++i) { |
159 | Object obj2 = obj1.arrayGet(i: 2 * i); |
160 | if (!obj2.isNum()) { |
161 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function range array" ); |
162 | return false; |
163 | } |
164 | range[i][0] = obj2.getNum(); |
165 | obj2 = obj1.arrayGet(i: 2 * i + 1); |
166 | if (!obj2.isNum()) { |
167 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function range array" ); |
168 | return false; |
169 | } |
170 | range[i][1] = obj2.getNum(); |
171 | } |
172 | } |
173 | |
174 | return true; |
175 | } |
176 | |
177 | //------------------------------------------------------------------------ |
178 | // IdentityFunction |
179 | //------------------------------------------------------------------------ |
180 | |
181 | IdentityFunction::IdentityFunction() |
182 | { |
183 | int i; |
184 | |
185 | // fill these in with arbitrary values just in case they get used |
186 | // somewhere |
187 | m = funcMaxInputs; |
188 | n = funcMaxOutputs; |
189 | for (i = 0; i < funcMaxInputs; ++i) { |
190 | domain[i][0] = 0; |
191 | domain[i][1] = 1; |
192 | } |
193 | hasRange = false; |
194 | } |
195 | |
196 | IdentityFunction::~IdentityFunction() { } |
197 | |
198 | void IdentityFunction::transform(const double *in, double *out) const |
199 | { |
200 | int i; |
201 | |
202 | for (i = 0; i < funcMaxOutputs; ++i) { |
203 | out[i] = in[i]; |
204 | } |
205 | } |
206 | |
207 | //------------------------------------------------------------------------ |
208 | // SampledFunction |
209 | //------------------------------------------------------------------------ |
210 | |
211 | SampledFunction::SampledFunction(Object *funcObj, Dict *dict) : cacheOut {} |
212 | { |
213 | Stream *str; |
214 | int sampleBits; |
215 | double sampleMul; |
216 | Object obj1; |
217 | unsigned int buf, bitMask; |
218 | int bits; |
219 | unsigned int s; |
220 | double in[funcMaxInputs]; |
221 | int i, j, t, bit, idx; |
222 | |
223 | idxOffset = nullptr; |
224 | samples = nullptr; |
225 | sBuf = nullptr; |
226 | ok = false; |
227 | |
228 | //----- initialize the generic stuff |
229 | if (!init(dict)) { |
230 | return; |
231 | } |
232 | if (!hasRange) { |
233 | error(category: errSyntaxError, pos: -1, msg: "Type 0 function is missing range" ); |
234 | return; |
235 | } |
236 | if (m > sampledFuncMaxInputs) { |
237 | error(category: errSyntaxError, pos: -1, msg: "Sampled functions with more than {0:d} inputs are unsupported" , sampledFuncMaxInputs); |
238 | return; |
239 | } |
240 | |
241 | //----- buffer |
242 | sBuf = (double *)gmallocn(count: 1 << m, size: sizeof(double)); |
243 | |
244 | //----- get the stream |
245 | if (!funcObj->isStream()) { |
246 | error(category: errSyntaxError, pos: -1, msg: "Type 0 function isn't a stream" ); |
247 | return; |
248 | } |
249 | str = funcObj->getStream(); |
250 | |
251 | //----- Size |
252 | obj1 = dict->lookup(key: "Size" ); |
253 | if (!obj1.isArray() || obj1.arrayGetLength() != m) { |
254 | error(category: errSyntaxError, pos: -1, msg: "Function has missing or invalid size array" ); |
255 | return; |
256 | } |
257 | for (i = 0; i < m; ++i) { |
258 | Object obj2 = obj1.arrayGet(i); |
259 | if (!obj2.isInt()) { |
260 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function size array" ); |
261 | return; |
262 | } |
263 | sampleSize[i] = obj2.getInt(); |
264 | if (sampleSize[i] <= 0) { |
265 | error(category: errSyntaxError, pos: -1, msg: "Illegal non-positive value in function size array" ); |
266 | return; |
267 | } |
268 | } |
269 | idxOffset = (int *)gmallocn(count: 1 << m, size: sizeof(int)); |
270 | for (i = 0; i < (1 << m); ++i) { |
271 | idx = 0; |
272 | for (j = m - 1, t = i; j >= 1; --j, t <<= 1) { |
273 | if (sampleSize[j] == 1) { |
274 | bit = 0; |
275 | } else { |
276 | bit = (t >> (m - 1)) & 1; |
277 | } |
278 | idx = (idx + bit) * sampleSize[j - 1]; |
279 | } |
280 | if (m > 0 && sampleSize[0] == 1) { |
281 | bit = 0; |
282 | } else { |
283 | bit = (t >> (m - 1)) & 1; |
284 | } |
285 | idxOffset[i] = (idx + bit) * n; |
286 | } |
287 | |
288 | //----- BitsPerSample |
289 | obj1 = dict->lookup(key: "BitsPerSample" ); |
290 | if (!obj1.isInt()) { |
291 | error(category: errSyntaxError, pos: -1, msg: "Function has missing or invalid BitsPerSample" ); |
292 | return; |
293 | } |
294 | sampleBits = obj1.getInt(); |
295 | if (unlikely(sampleBits < 1 || sampleBits > 32)) { |
296 | error(category: errSyntaxError, pos: -1, msg: "Function invalid BitsPerSample" ); |
297 | return; |
298 | } |
299 | sampleMul = 1.0 / (pow(x: 2.0, y: (double)sampleBits) - 1); |
300 | |
301 | //----- Encode |
302 | obj1 = dict->lookup(key: "Encode" ); |
303 | if (obj1.isArray() && obj1.arrayGetLength() == 2 * m) { |
304 | for (i = 0; i < m; ++i) { |
305 | Object obj2 = obj1.arrayGet(i: 2 * i); |
306 | if (!obj2.isNum()) { |
307 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function encode array" ); |
308 | return; |
309 | } |
310 | encode[i][0] = obj2.getNum(); |
311 | obj2 = obj1.arrayGet(i: 2 * i + 1); |
312 | if (!obj2.isNum()) { |
313 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function encode array" ); |
314 | return; |
315 | } |
316 | encode[i][1] = obj2.getNum(); |
317 | } |
318 | } else { |
319 | for (i = 0; i < m; ++i) { |
320 | encode[i][0] = 0; |
321 | encode[i][1] = sampleSize[i] - 1; |
322 | } |
323 | } |
324 | for (i = 0; i < m; ++i) { |
325 | if (unlikely((domain[i][1] - domain[i][0]) == 0)) { |
326 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function domain array" ); |
327 | return; |
328 | } |
329 | inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); |
330 | } |
331 | |
332 | //----- Decode |
333 | obj1 = dict->lookup(key: "Decode" ); |
334 | if (obj1.isArray() && obj1.arrayGetLength() == 2 * n) { |
335 | for (i = 0; i < n; ++i) { |
336 | Object obj2 = obj1.arrayGet(i: 2 * i); |
337 | if (!obj2.isNum()) { |
338 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function decode array" ); |
339 | return; |
340 | } |
341 | decode[i][0] = obj2.getNum(); |
342 | obj2 = obj1.arrayGet(i: 2 * i + 1); |
343 | if (!obj2.isNum()) { |
344 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function decode array" ); |
345 | return; |
346 | } |
347 | decode[i][1] = obj2.getNum(); |
348 | } |
349 | } else { |
350 | for (i = 0; i < n; ++i) { |
351 | decode[i][0] = range[i][0]; |
352 | decode[i][1] = range[i][1]; |
353 | } |
354 | } |
355 | |
356 | //----- samples |
357 | nSamples = n; |
358 | for (i = 0; i < m; ++i) { |
359 | nSamples *= sampleSize[i]; |
360 | } |
361 | samples = (double *)gmallocn_checkoverflow(count: nSamples, size: sizeof(double)); |
362 | if (!samples) { |
363 | error(category: errSyntaxError, pos: -1, msg: "Function has invalid number of samples" ); |
364 | return; |
365 | } |
366 | buf = 0; |
367 | bits = 0; |
368 | bitMask = (1 << sampleBits) - 1; |
369 | str->reset(); |
370 | for (i = 0; i < nSamples; ++i) { |
371 | if (sampleBits == 8) { |
372 | s = str->getChar(); |
373 | } else if (sampleBits == 16) { |
374 | s = str->getChar(); |
375 | s = (s << 8) + str->getChar(); |
376 | } else if (sampleBits == 32) { |
377 | s = str->getChar(); |
378 | s = (s << 8) + str->getChar(); |
379 | s = (s << 8) + str->getChar(); |
380 | s = (s << 8) + str->getChar(); |
381 | } else { |
382 | while (bits < sampleBits) { |
383 | buf = (buf << 8) | (str->getChar() & 0xff); |
384 | bits += 8; |
385 | } |
386 | s = (buf >> (bits - sampleBits)) & bitMask; |
387 | bits -= sampleBits; |
388 | } |
389 | samples[i] = (double)s * sampleMul; |
390 | } |
391 | str->close(); |
392 | |
393 | // set up the cache |
394 | for (i = 0; i < m; ++i) { |
395 | in[i] = domain[i][0]; |
396 | cacheIn[i] = in[i] - 1; |
397 | } |
398 | transform(in, out: cacheOut); |
399 | |
400 | ok = true; |
401 | } |
402 | |
403 | SampledFunction::~SampledFunction() |
404 | { |
405 | if (idxOffset) { |
406 | gfree(p: idxOffset); |
407 | } |
408 | if (samples) { |
409 | gfree(p: samples); |
410 | } |
411 | if (sBuf) { |
412 | gfree(p: sBuf); |
413 | } |
414 | } |
415 | |
416 | SampledFunction::SampledFunction(const SampledFunction *func) : Function(func) |
417 | { |
418 | memcpy(dest: sampleSize, src: func->sampleSize, funcMaxInputs * sizeof(int)); |
419 | |
420 | memcpy(dest: encode, src: func->encode, funcMaxInputs * 2 * sizeof(double)); |
421 | memcpy(dest: decode, src: func->decode, funcMaxOutputs * 2 * sizeof(double)); |
422 | |
423 | memcpy(dest: inputMul, src: func->inputMul, funcMaxInputs * sizeof(double)); |
424 | |
425 | nSamples = func->nSamples; |
426 | |
427 | idxOffset = (int *)gmallocn(count: 1 << m, size: sizeof(int)); |
428 | memcpy(dest: idxOffset, src: func->idxOffset, n: (1 << m) * (int)sizeof(int)); |
429 | |
430 | samples = (double *)gmallocn(count: nSamples, size: sizeof(double)); |
431 | memcpy(dest: samples, src: func->samples, n: nSamples * sizeof(double)); |
432 | |
433 | sBuf = (double *)gmallocn(count: 1 << m, size: sizeof(double)); |
434 | |
435 | memcpy(dest: cacheIn, src: func->cacheIn, funcMaxInputs * sizeof(double)); |
436 | memcpy(dest: cacheOut, src: func->cacheOut, funcMaxOutputs * sizeof(double)); |
437 | |
438 | ok = func->ok; |
439 | } |
440 | |
441 | void SampledFunction::transform(const double *in, double *out) const |
442 | { |
443 | double x; |
444 | int e[funcMaxInputs]; |
445 | double efrac0[funcMaxInputs]; |
446 | double efrac1[funcMaxInputs]; |
447 | |
448 | // check the cache |
449 | bool inCache = true; |
450 | for (int i = 0; i < m; ++i) { |
451 | if (in[i] != cacheIn[i]) { |
452 | inCache = false; |
453 | break; |
454 | } |
455 | } |
456 | if (inCache) { |
457 | for (int i = 0; i < n; ++i) { |
458 | out[i] = cacheOut[i]; |
459 | } |
460 | return; |
461 | } |
462 | |
463 | // map input values into sample array |
464 | for (int i = 0; i < m; ++i) { |
465 | x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; |
466 | if (x < 0 || std::isnan(x: x)) { |
467 | x = 0; |
468 | } else if (x > sampleSize[i] - 1) { |
469 | x = sampleSize[i] - 1; |
470 | } |
471 | e[i] = (int)x; |
472 | if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { |
473 | // this happens if in[i] = domain[i][1] |
474 | e[i] = sampleSize[i] - 2; |
475 | } |
476 | efrac1[i] = x - e[i]; |
477 | efrac0[i] = 1 - efrac1[i]; |
478 | } |
479 | |
480 | // compute index for the first sample to be used |
481 | int idx0 = 0; |
482 | for (int k = m - 1; k >= 1; --k) { |
483 | idx0 = (idx0 + e[k]) * sampleSize[k - 1]; |
484 | } |
485 | idx0 = (idx0 + e[0]) * n; |
486 | |
487 | // for each output, do m-linear interpolation |
488 | for (int i = 0; i < n; ++i) { |
489 | |
490 | // pull 2^m values out of the sample array |
491 | for (int j = 0; j < (1 << m); ++j) { |
492 | int idx = idx0 + idxOffset[j] + i; |
493 | if (likely(idx >= 0 && idx < nSamples)) { |
494 | sBuf[j] = samples[idx]; |
495 | } else { |
496 | sBuf[j] = 0; // TODO Investigate if this is what Adobe does |
497 | } |
498 | } |
499 | |
500 | // do m sets of interpolations |
501 | for (int j = 0, t = (1 << m); j < m; ++j, t >>= 1) { |
502 | for (int k = 0; k < t; k += 2) { |
503 | sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k + 1]; |
504 | } |
505 | } |
506 | |
507 | // map output value to range |
508 | out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; |
509 | if (out[i] < range[i][0]) { |
510 | out[i] = range[i][0]; |
511 | } else if (out[i] > range[i][1]) { |
512 | out[i] = range[i][1]; |
513 | } |
514 | } |
515 | |
516 | // save current result in the cache |
517 | for (int i = 0; i < m; ++i) { |
518 | cacheIn[i] = in[i]; |
519 | } |
520 | for (int i = 0; i < n; ++i) { |
521 | cacheOut[i] = out[i]; |
522 | } |
523 | } |
524 | |
525 | bool SampledFunction::hasDifferentResultSet(const Function *func) const |
526 | { |
527 | if (func->getType() == Type::Sampled) { |
528 | SampledFunction *compTo = (SampledFunction *)func; |
529 | if (compTo->getSampleNumber() != nSamples) { |
530 | return true; |
531 | } |
532 | const double *compSamples = compTo->getSamples(); |
533 | for (int i = 0; i < nSamples; i++) { |
534 | if (samples[i] != compSamples[i]) { |
535 | return true; |
536 | } |
537 | } |
538 | } |
539 | return false; |
540 | } |
541 | |
542 | //------------------------------------------------------------------------ |
543 | // ExponentialFunction |
544 | //------------------------------------------------------------------------ |
545 | |
546 | ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) |
547 | { |
548 | Object obj1; |
549 | |
550 | ok = false; |
551 | |
552 | //----- initialize the generic stuff |
553 | if (!init(dict)) { |
554 | return; |
555 | } |
556 | if (m != 1) { |
557 | error(category: errSyntaxError, pos: -1, msg: "Exponential function with more than one input" ); |
558 | return; |
559 | } |
560 | |
561 | //----- C0 |
562 | obj1 = dict->lookup(key: "C0" ); |
563 | if (obj1.isArray()) { |
564 | if (hasRange && obj1.arrayGetLength() != n) { |
565 | error(category: errSyntaxError, pos: -1, msg: "Function's C0 array is wrong length" ); |
566 | return; |
567 | } |
568 | n = obj1.arrayGetLength(); |
569 | if (unlikely(n > funcMaxOutputs)) { |
570 | error(category: errSyntaxError, pos: -1, msg: "Function's C0 array is wrong length" ); |
571 | n = funcMaxOutputs; |
572 | } |
573 | for (int i = 0; i < n; ++i) { |
574 | Object obj2 = obj1.arrayGet(i); |
575 | if (!obj2.isNum()) { |
576 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function C0 array" ); |
577 | return; |
578 | } |
579 | c0[i] = obj2.getNum(); |
580 | } |
581 | } else { |
582 | if (hasRange && n != 1) { |
583 | error(category: errSyntaxError, pos: -1, msg: "Function's C0 array is wrong length" ); |
584 | return; |
585 | } |
586 | n = 1; |
587 | c0[0] = 0; |
588 | } |
589 | |
590 | //----- C1 |
591 | obj1 = dict->lookup(key: "C1" ); |
592 | if (obj1.isArray()) { |
593 | if (obj1.arrayGetLength() != n) { |
594 | error(category: errSyntaxError, pos: -1, msg: "Function's C1 array is wrong length" ); |
595 | return; |
596 | } |
597 | for (int i = 0; i < n; ++i) { |
598 | Object obj2 = obj1.arrayGet(i); |
599 | if (!obj2.isNum()) { |
600 | error(category: errSyntaxError, pos: -1, msg: "Illegal value in function C1 array" ); |
601 | return; |
602 | } |
603 | c1[i] = obj2.getNum(); |
604 | } |
605 | } else { |
606 | if (n != 1) { |
607 | error(category: errSyntaxError, pos: -1, msg: "Function's C1 array is wrong length" ); |
608 | return; |
609 | } |
610 | c1[0] = 1; |
611 | } |
612 | |
613 | //----- N (exponent) |
614 | obj1 = dict->lookup(key: "N" ); |
615 | if (!obj1.isNum()) { |
616 | error(category: errSyntaxError, pos: -1, msg: "Function has missing or invalid N" ); |
617 | return; |
618 | } |
619 | e = obj1.getNum(); |
620 | |
621 | isLinear = fabs(x: e - 1.) < 1e-10; |
622 | ok = true; |
623 | } |
624 | |
625 | ExponentialFunction::~ExponentialFunction() { } |
626 | |
627 | ExponentialFunction::ExponentialFunction(const ExponentialFunction *func) : Function(func) |
628 | { |
629 | memcpy(dest: c0, src: func->c0, funcMaxOutputs * sizeof(double)); |
630 | memcpy(dest: c1, src: func->c1, funcMaxOutputs * sizeof(double)); |
631 | |
632 | e = func->e; |
633 | isLinear = func->isLinear; |
634 | ok = func->ok; |
635 | } |
636 | |
637 | void ExponentialFunction::transform(const double *in, double *out) const |
638 | { |
639 | double x; |
640 | int i; |
641 | |
642 | if (in[0] < domain[0][0]) { |
643 | x = domain[0][0]; |
644 | } else if (in[0] > domain[0][1]) { |
645 | x = domain[0][1]; |
646 | } else { |
647 | x = in[0]; |
648 | } |
649 | for (i = 0; i < n; ++i) { |
650 | out[i] = c0[i] + (isLinear ? x : pow(x: x, y: e)) * (c1[i] - c0[i]); |
651 | if (hasRange) { |
652 | if (out[i] < range[i][0]) { |
653 | out[i] = range[i][0]; |
654 | } else if (out[i] > range[i][1]) { |
655 | out[i] = range[i][1]; |
656 | } |
657 | } |
658 | } |
659 | return; |
660 | } |
661 | |
662 | //------------------------------------------------------------------------ |
663 | // StitchingFunction |
664 | //------------------------------------------------------------------------ |
665 | |
666 | StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set<int> *usedParents) |
667 | { |
668 | Object obj1; |
669 | int i; |
670 | |
671 | ok = false; |
672 | funcs = nullptr; |
673 | bounds = nullptr; |
674 | encode = nullptr; |
675 | scale = nullptr; |
676 | |
677 | //----- initialize the generic stuff |
678 | if (!init(dict)) { |
679 | return; |
680 | } |
681 | if (m != 1) { |
682 | error(category: errSyntaxError, pos: -1, msg: "Stitching function with more than one input" ); |
683 | return; |
684 | } |
685 | |
686 | //----- Functions |
687 | obj1 = dict->lookup(key: "Functions" ); |
688 | if (!obj1.isArray()) { |
689 | error(category: errSyntaxError, pos: -1, msg: "Missing 'Functions' entry in stitching function" ); |
690 | return; |
691 | } |
692 | k = obj1.arrayGetLength(); |
693 | funcs = (Function **)gmallocn(count: k, size: sizeof(Function *)); |
694 | bounds = (double *)gmallocn(count: k + 1, size: sizeof(double)); |
695 | encode = (double *)gmallocn(count: 2 * k, size: sizeof(double)); |
696 | scale = (double *)gmallocn(count: k, size: sizeof(double)); |
697 | for (i = 0; i < k; ++i) { |
698 | funcs[i] = nullptr; |
699 | } |
700 | for (i = 0; i < k; ++i) { |
701 | std::set<int> usedParentsAux = *usedParents; |
702 | Ref ref; |
703 | Object obj2 = obj1.getArray()->get(i, returnRef: &ref); |
704 | if (ref != Ref::INVALID()) { |
705 | if (usedParentsAux.find(x: ref.num) == usedParentsAux.end()) { |
706 | usedParentsAux.insert(x: ref.num); |
707 | } else { |
708 | return; |
709 | } |
710 | } |
711 | if (!(funcs[i] = Function::parse(funcObj: &obj2, usedParents: &usedParentsAux))) { |
712 | return; |
713 | } |
714 | if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { |
715 | error(category: errSyntaxError, pos: -1, msg: "Incompatible subfunctions in stitching function" ); |
716 | return; |
717 | } |
718 | } |
719 | |
720 | //----- Bounds |
721 | obj1 = dict->lookup(key: "Bounds" ); |
722 | if (!obj1.isArray() || obj1.arrayGetLength() != k - 1) { |
723 | error(category: errSyntaxError, pos: -1, msg: "Missing or invalid 'Bounds' entry in stitching function" ); |
724 | return; |
725 | } |
726 | bounds[0] = domain[0][0]; |
727 | for (i = 1; i < k; ++i) { |
728 | Object obj2 = obj1.arrayGet(i: i - 1); |
729 | if (!obj2.isNum()) { |
730 | error(category: errSyntaxError, pos: -1, msg: "Invalid type in 'Bounds' array in stitching function" ); |
731 | return; |
732 | } |
733 | bounds[i] = obj2.getNum(); |
734 | } |
735 | bounds[k] = domain[0][1]; |
736 | |
737 | //----- Encode |
738 | obj1 = dict->lookup(key: "Encode" ); |
739 | if (!obj1.isArray() || obj1.arrayGetLength() != 2 * k) { |
740 | error(category: errSyntaxError, pos: -1, msg: "Missing or invalid 'Encode' entry in stitching function" ); |
741 | return; |
742 | } |
743 | for (i = 0; i < 2 * k; ++i) { |
744 | Object obj2 = obj1.arrayGet(i); |
745 | if (!obj2.isNum()) { |
746 | error(category: errSyntaxError, pos: -1, msg: "Invalid type in 'Encode' array in stitching function" ); |
747 | return; |
748 | } |
749 | encode[i] = obj2.getNum(); |
750 | } |
751 | |
752 | //----- pre-compute the scale factors |
753 | for (i = 0; i < k; ++i) { |
754 | if (bounds[i] == bounds[i + 1]) { |
755 | // avoid a divide-by-zero -- in this situation, function i will |
756 | // never be used anyway |
757 | scale[i] = 0; |
758 | } else { |
759 | scale[i] = (encode[2 * i + 1] - encode[2 * i]) / (bounds[i + 1] - bounds[i]); |
760 | } |
761 | } |
762 | |
763 | n = funcs[0]->getOutputSize(); |
764 | ok = true; |
765 | return; |
766 | } |
767 | |
768 | StitchingFunction::StitchingFunction(const StitchingFunction *func) : Function(func) |
769 | { |
770 | k = func->k; |
771 | |
772 | funcs = (Function **)gmallocn(count: k, size: sizeof(Function *)); |
773 | for (int i = 0; i < k; ++i) { |
774 | funcs[i] = func->funcs[i]->copy(); |
775 | } |
776 | |
777 | bounds = (double *)gmallocn(count: k + 1, size: sizeof(double)); |
778 | memcpy(dest: bounds, src: func->bounds, n: (k + 1) * sizeof(double)); |
779 | |
780 | encode = (double *)gmallocn(count: 2 * k, size: sizeof(double)); |
781 | memcpy(dest: encode, src: func->encode, n: 2 * k * sizeof(double)); |
782 | |
783 | scale = (double *)gmallocn(count: k, size: sizeof(double)); |
784 | memcpy(dest: scale, src: func->scale, n: k * sizeof(double)); |
785 | |
786 | ok = func->ok; |
787 | } |
788 | |
789 | StitchingFunction::~StitchingFunction() |
790 | { |
791 | int i; |
792 | |
793 | if (funcs) { |
794 | for (i = 0; i < k; ++i) { |
795 | if (funcs[i]) { |
796 | delete funcs[i]; |
797 | } |
798 | } |
799 | } |
800 | gfree(p: funcs); |
801 | gfree(p: bounds); |
802 | gfree(p: encode); |
803 | gfree(p: scale); |
804 | } |
805 | |
806 | void StitchingFunction::transform(const double *in, double *out) const |
807 | { |
808 | double x; |
809 | int i; |
810 | |
811 | if (in[0] < domain[0][0]) { |
812 | x = domain[0][0]; |
813 | } else if (in[0] > domain[0][1]) { |
814 | x = domain[0][1]; |
815 | } else { |
816 | x = in[0]; |
817 | } |
818 | for (i = 0; i < k - 1; ++i) { |
819 | if (x < bounds[i + 1]) { |
820 | break; |
821 | } |
822 | } |
823 | x = encode[2 * i] + (x - bounds[i]) * scale[i]; |
824 | funcs[i]->transform(in: &x, out); |
825 | } |
826 | |
827 | //------------------------------------------------------------------------ |
828 | // PostScriptFunction |
829 | //------------------------------------------------------------------------ |
830 | |
831 | enum PSOp |
832 | { |
833 | psOpAbs, |
834 | psOpAdd, |
835 | psOpAnd, |
836 | psOpAtan, |
837 | psOpBitshift, |
838 | psOpCeiling, |
839 | psOpCopy, |
840 | psOpCos, |
841 | psOpCvi, |
842 | psOpCvr, |
843 | psOpDiv, |
844 | psOpDup, |
845 | psOpEq, |
846 | psOpExch, |
847 | psOpExp, |
848 | psOpFalse, |
849 | psOpFloor, |
850 | psOpGe, |
851 | psOpGt, |
852 | psOpIdiv, |
853 | psOpIndex, |
854 | psOpLe, |
855 | psOpLn, |
856 | psOpLog, |
857 | psOpLt, |
858 | psOpMod, |
859 | psOpMul, |
860 | psOpNe, |
861 | psOpNeg, |
862 | psOpNot, |
863 | psOpOr, |
864 | psOpPop, |
865 | psOpRoll, |
866 | psOpRound, |
867 | psOpSin, |
868 | psOpSqrt, |
869 | psOpSub, |
870 | psOpTrue, |
871 | psOpTruncate, |
872 | psOpXor, |
873 | psOpIf, |
874 | psOpIfelse, |
875 | psOpReturn |
876 | }; |
877 | |
878 | // Note: 'if' and 'ifelse' are parsed separately. |
879 | // The rest are listed here in alphabetical order. |
880 | // The index in this table is equivalent to the entry in PSOp. |
881 | static const char *psOpNames[] = { "abs" , "add" , "and" , "atan" , "bitshift" , "ceiling" , "copy" , "cos" , "cvi" , "cvr" , "div" , "dup" , "eq" , "exch" , "exp" , "false" , "floor" , "ge" , "gt" , "idiv" , |
882 | "index" , "le" , "ln" , "log" , "lt" , "mod" , "mul" , "ne" , "neg" , "not" , "or" , "pop" , "roll" , "round" , "sin" , "sqrt" , "sub" , "true" , "truncate" , "xor" }; |
883 | |
884 | #define nPSOps (sizeof(psOpNames) / sizeof(char *)) |
885 | |
886 | enum PSObjectType |
887 | { |
888 | psBool, |
889 | psInt, |
890 | psReal, |
891 | psOperator, |
892 | psBlock |
893 | }; |
894 | |
895 | // In the code array, 'if'/'ifelse' operators take up three slots |
896 | // plus space for the code in the subclause(s). |
897 | // |
898 | // +---------------------------------+ |
899 | // | psOperator: psOpIf / psOpIfelse | |
900 | // +---------------------------------+ |
901 | // | psBlock: ptr=<A> | |
902 | // +---------------------------------+ |
903 | // | psBlock: ptr=<B> | |
904 | // +---------------------------------+ |
905 | // | if clause | |
906 | // | ... | |
907 | // | psOperator: psOpReturn | |
908 | // +---------------------------------+ |
909 | // <A> | else clause | |
910 | // | ... | |
911 | // | psOperator: psOpReturn | |
912 | // +---------------------------------+ |
913 | // <B> | ... | |
914 | // |
915 | // For 'if', pointer <A> is present in the code stream but unused. |
916 | |
917 | struct PSObject |
918 | { |
919 | PSObjectType type; |
920 | union { |
921 | bool booln; // boolean (stack only) |
922 | int intg; // integer (stack and code) |
923 | double real; // real (stack and code) |
924 | PSOp op; // operator (code only) |
925 | int blk; // if/ifelse block pointer (code only) |
926 | }; |
927 | }; |
928 | |
929 | #define psStackSize 100 |
930 | |
931 | class PSStack |
932 | { |
933 | public: |
934 | PSStack() { sp = psStackSize; } |
935 | void clear() { sp = psStackSize; } |
936 | void pushBool(bool booln) |
937 | { |
938 | if (checkOverflow()) { |
939 | stack[--sp].type = psBool; |
940 | stack[sp].booln = booln; |
941 | } |
942 | } |
943 | void pushInt(int intg) |
944 | { |
945 | if (checkOverflow()) { |
946 | stack[--sp].type = psInt; |
947 | stack[sp].intg = intg; |
948 | } |
949 | } |
950 | void pushReal(double real) |
951 | { |
952 | if (checkOverflow()) { |
953 | stack[--sp].type = psReal; |
954 | stack[sp].real = real; |
955 | } |
956 | } |
957 | bool popBool() |
958 | { |
959 | if (checkUnderflow() && checkType(t1: psBool, t2: psBool)) { |
960 | return stack[sp++].booln; |
961 | } |
962 | return false; |
963 | } |
964 | int popInt() |
965 | { |
966 | if (checkUnderflow() && checkType(t1: psInt, t2: psInt)) { |
967 | return stack[sp++].intg; |
968 | } |
969 | return 0; |
970 | } |
971 | double popNum() |
972 | { |
973 | double ret; |
974 | |
975 | if (checkUnderflow() && checkType(t1: psInt, t2: psReal)) { |
976 | ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real; |
977 | ++sp; |
978 | return ret; |
979 | } |
980 | return 0; |
981 | } |
982 | bool empty() { return sp == psStackSize; } |
983 | bool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; } |
984 | bool topTwoAreInts() { return sp < psStackSize - 1 && stack[sp].type == psInt && stack[sp + 1].type == psInt; } |
985 | bool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; } |
986 | bool topTwoAreNums() { return sp < psStackSize - 1 && (stack[sp].type == psInt || stack[sp].type == psReal) && (stack[sp + 1].type == psInt || stack[sp + 1].type == psReal); } |
987 | void copy(int n); |
988 | void roll(int n, int j); |
989 | void index(int i) |
990 | { |
991 | if (!checkOverflow()) { |
992 | return; |
993 | } |
994 | --sp; |
995 | if (unlikely(sp + i + 1 >= psStackSize)) { |
996 | error(category: errSyntaxError, pos: -1, msg: "Stack underflow in PostScript function" ); |
997 | return; |
998 | } |
999 | if (unlikely(sp + i + 1 < 0)) { |
1000 | error(category: errSyntaxError, pos: -1, msg: "Stack overflow in PostScript function" ); |
1001 | return; |
1002 | } |
1003 | stack[sp] = stack[sp + 1 + i]; |
1004 | } |
1005 | void pop() |
1006 | { |
1007 | if (!checkUnderflow()) { |
1008 | return; |
1009 | } |
1010 | ++sp; |
1011 | } |
1012 | |
1013 | private: |
1014 | bool checkOverflow(int n = 1) |
1015 | { |
1016 | if (sp - n < 0) { |
1017 | error(category: errSyntaxError, pos: -1, msg: "Stack overflow in PostScript function" ); |
1018 | return false; |
1019 | } |
1020 | return true; |
1021 | } |
1022 | bool checkUnderflow() |
1023 | { |
1024 | if (sp == psStackSize) { |
1025 | error(category: errSyntaxError, pos: -1, msg: "Stack underflow in PostScript function" ); |
1026 | return false; |
1027 | } |
1028 | return true; |
1029 | } |
1030 | bool checkType(PSObjectType t1, PSObjectType t2) |
1031 | { |
1032 | if (stack[sp].type != t1 && stack[sp].type != t2) { |
1033 | error(category: errSyntaxError, pos: -1, msg: "Type mismatch in PostScript function" ); |
1034 | return false; |
1035 | } |
1036 | return true; |
1037 | } |
1038 | PSObject stack[psStackSize]; |
1039 | int sp; |
1040 | }; |
1041 | |
1042 | void PSStack::copy(int n) |
1043 | { |
1044 | int i; |
1045 | |
1046 | int aux; |
1047 | if (unlikely(checkedAdd(sp, n, &aux) || aux > psStackSize)) { |
1048 | error(category: errSyntaxError, pos: -1, msg: "Stack underflow in PostScript function" ); |
1049 | return; |
1050 | } |
1051 | if (unlikely(checkedSubtraction(sp, n, &aux) || aux > psStackSize)) { |
1052 | error(category: errSyntaxError, pos: -1, msg: "Stack underflow in PostScript function" ); |
1053 | return; |
1054 | } |
1055 | if (!checkOverflow(n)) { |
1056 | return; |
1057 | } |
1058 | for (i = sp + n - 1; i >= sp; --i) { |
1059 | stack[i - n] = stack[i]; |
1060 | } |
1061 | sp -= n; |
1062 | } |
1063 | |
1064 | void PSStack::roll(int n, int j) |
1065 | { |
1066 | PSObject obj; |
1067 | int i, k; |
1068 | |
1069 | if (unlikely(n == 0)) { |
1070 | return; |
1071 | } |
1072 | if (j >= 0) { |
1073 | j %= n; |
1074 | } else { |
1075 | j = -j % n; |
1076 | if (j != 0) { |
1077 | j = n - j; |
1078 | } |
1079 | } |
1080 | if (n <= 0 || j == 0 || n > psStackSize || sp + n > psStackSize) { |
1081 | return; |
1082 | } |
1083 | if (j <= n / 2) { |
1084 | for (i = 0; i < j; ++i) { |
1085 | obj = stack[sp]; |
1086 | for (k = sp; k < sp + n - 1; ++k) { |
1087 | stack[k] = stack[k + 1]; |
1088 | } |
1089 | stack[sp + n - 1] = obj; |
1090 | } |
1091 | } else { |
1092 | j = n - j; |
1093 | for (i = 0; i < j; ++i) { |
1094 | obj = stack[sp + n - 1]; |
1095 | for (k = sp + n - 1; k > sp; --k) { |
1096 | stack[k] = stack[k - 1]; |
1097 | } |
1098 | stack[sp] = obj; |
1099 | } |
1100 | } |
1101 | } |
1102 | |
1103 | PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) |
1104 | { |
1105 | Stream *str; |
1106 | int codePtr; |
1107 | double in[funcMaxInputs]; |
1108 | int i; |
1109 | |
1110 | code = nullptr; |
1111 | codeString = nullptr; |
1112 | codeSize = 0; |
1113 | ok = false; |
1114 | |
1115 | //----- initialize the generic stuff |
1116 | if (!init(dict)) { |
1117 | goto err1; |
1118 | } |
1119 | if (!hasRange) { |
1120 | error(category: errSyntaxError, pos: -1, msg: "Type 4 function is missing range" ); |
1121 | goto err1; |
1122 | } |
1123 | |
1124 | //----- get the stream |
1125 | if (!funcObj->isStream()) { |
1126 | error(category: errSyntaxError, pos: -1, msg: "Type 4 function isn't a stream" ); |
1127 | goto err1; |
1128 | } |
1129 | str = funcObj->getStream(); |
1130 | |
1131 | //----- parse the function |
1132 | codeString = new GooString(); |
1133 | str->reset(); |
1134 | if (getToken(str)->cmp(sA: "{" ) != 0) { |
1135 | error(category: errSyntaxError, pos: -1, msg: "Expected '{{' at start of PostScript function" ); |
1136 | goto err1; |
1137 | } |
1138 | codePtr = 0; |
1139 | if (!parseCode(str, codePtr: &codePtr)) { |
1140 | goto err2; |
1141 | } |
1142 | str->close(); |
1143 | |
1144 | //----- set up the cache |
1145 | for (i = 0; i < m; ++i) { |
1146 | in[i] = domain[i][0]; |
1147 | cacheIn[i] = in[i] - 1; |
1148 | } |
1149 | transform(in, out: cacheOut); |
1150 | |
1151 | ok = true; |
1152 | |
1153 | err2: |
1154 | str->close(); |
1155 | err1: |
1156 | return; |
1157 | } |
1158 | |
1159 | PostScriptFunction::PostScriptFunction(const PostScriptFunction *func) : Function(func) |
1160 | { |
1161 | codeSize = func->codeSize; |
1162 | |
1163 | code = (PSObject *)gmallocn(count: codeSize, size: sizeof(PSObject)); |
1164 | memcpy(dest: code, src: func->code, n: codeSize * sizeof(PSObject)); |
1165 | |
1166 | codeString = func->codeString->copy(); |
1167 | |
1168 | memcpy(dest: cacheIn, src: func->cacheIn, funcMaxInputs * sizeof(double)); |
1169 | memcpy(dest: cacheOut, src: func->cacheOut, funcMaxOutputs * sizeof(double)); |
1170 | |
1171 | ok = func->ok; |
1172 | } |
1173 | |
1174 | PostScriptFunction::~PostScriptFunction() |
1175 | { |
1176 | gfree(p: code); |
1177 | delete codeString; |
1178 | } |
1179 | |
1180 | void PostScriptFunction::transform(const double *in, double *out) const |
1181 | { |
1182 | PSStack stack; |
1183 | int i; |
1184 | |
1185 | // check the cache |
1186 | for (i = 0; i < m; ++i) { |
1187 | if (in[i] != cacheIn[i]) { |
1188 | break; |
1189 | } |
1190 | } |
1191 | if (i == m) { |
1192 | for (i = 0; i < n; ++i) { |
1193 | out[i] = cacheOut[i]; |
1194 | } |
1195 | return; |
1196 | } |
1197 | |
1198 | for (i = 0; i < m; ++i) { |
1199 | //~ may need to check for integers here |
1200 | stack.pushReal(real: in[i]); |
1201 | } |
1202 | exec(stack: &stack, codePtr: 0); |
1203 | for (i = n - 1; i >= 0; --i) { |
1204 | out[i] = stack.popNum(); |
1205 | if (out[i] < range[i][0]) { |
1206 | out[i] = range[i][0]; |
1207 | } else if (out[i] > range[i][1]) { |
1208 | out[i] = range[i][1]; |
1209 | } |
1210 | } |
1211 | stack.clear(); |
1212 | |
1213 | // if (!stack->empty()) { |
1214 | // error(errSyntaxWarning, -1, |
1215 | // "Extra values on stack at end of PostScript function"); |
1216 | // } |
1217 | |
1218 | // save current result in the cache |
1219 | for (i = 0; i < m; ++i) { |
1220 | cacheIn[i] = in[i]; |
1221 | } |
1222 | for (i = 0; i < n; ++i) { |
1223 | cacheOut[i] = out[i]; |
1224 | } |
1225 | } |
1226 | |
1227 | bool PostScriptFunction::parseCode(Stream *str, int *codePtr) |
1228 | { |
1229 | bool isReal; |
1230 | int opPtr, elsePtr; |
1231 | int a, b, mid, cmp; |
1232 | |
1233 | while (true) { |
1234 | // This needs to be on the heap to help make parseCode |
1235 | // able to call itself more times recursively |
1236 | std::unique_ptr<GooString> tok = getToken(str); |
1237 | const char *p = tok->c_str(); |
1238 | if (isdigit(*p) || *p == '.' || *p == '-') { |
1239 | isReal = false; |
1240 | for (; *p; ++p) { |
1241 | if (*p == '.') { |
1242 | isReal = true; |
1243 | break; |
1244 | } |
1245 | } |
1246 | resizeCode(newSize: *codePtr); |
1247 | if (isReal) { |
1248 | code[*codePtr].type = psReal; |
1249 | code[*codePtr].real = gatof(nptr: tok->c_str()); |
1250 | } else { |
1251 | code[*codePtr].type = psInt; |
1252 | code[*codePtr].intg = atoi(nptr: tok->c_str()); |
1253 | } |
1254 | ++*codePtr; |
1255 | } else if (!tok->cmp(sA: "{" )) { |
1256 | opPtr = *codePtr; |
1257 | *codePtr += 3; |
1258 | resizeCode(newSize: opPtr + 2); |
1259 | if (!parseCode(str, codePtr)) { |
1260 | return false; |
1261 | } |
1262 | tok = getToken(str); |
1263 | if (!tok->cmp(sA: "{" )) { |
1264 | elsePtr = *codePtr; |
1265 | if (!parseCode(str, codePtr)) { |
1266 | return false; |
1267 | } |
1268 | tok = getToken(str); |
1269 | } else { |
1270 | elsePtr = -1; |
1271 | } |
1272 | if (!tok->cmp(sA: "if" )) { |
1273 | if (elsePtr >= 0) { |
1274 | error(category: errSyntaxError, pos: -1, msg: "Got 'if' operator with two blocks in PostScript function" ); |
1275 | return false; |
1276 | } |
1277 | code[opPtr].type = psOperator; |
1278 | code[opPtr].op = psOpIf; |
1279 | code[opPtr + 2].type = psBlock; |
1280 | code[opPtr + 2].blk = *codePtr; |
1281 | } else if (!tok->cmp(sA: "ifelse" )) { |
1282 | if (elsePtr < 0) { |
1283 | error(category: errSyntaxError, pos: -1, msg: "Got 'ifelse' operator with one block in PostScript function" ); |
1284 | return false; |
1285 | } |
1286 | code[opPtr].type = psOperator; |
1287 | code[opPtr].op = psOpIfelse; |
1288 | code[opPtr + 1].type = psBlock; |
1289 | code[opPtr + 1].blk = elsePtr; |
1290 | code[opPtr + 2].type = psBlock; |
1291 | code[opPtr + 2].blk = *codePtr; |
1292 | } else { |
1293 | error(category: errSyntaxError, pos: -1, msg: "Expected if/ifelse operator in PostScript function" ); |
1294 | return false; |
1295 | } |
1296 | } else if (!tok->cmp(sA: "}" )) { |
1297 | resizeCode(newSize: *codePtr); |
1298 | code[*codePtr].type = psOperator; |
1299 | code[*codePtr].op = psOpReturn; |
1300 | ++*codePtr; |
1301 | break; |
1302 | } else { |
1303 | a = -1; |
1304 | b = nPSOps; |
1305 | cmp = 0; // make gcc happy |
1306 | // invariant: psOpNames[a] < tok < psOpNames[b] |
1307 | while (b - a > 1) { |
1308 | mid = (a + b) / 2; |
1309 | cmp = tok->cmp(sA: psOpNames[mid]); |
1310 | if (cmp > 0) { |
1311 | a = mid; |
1312 | } else if (cmp < 0) { |
1313 | b = mid; |
1314 | } else { |
1315 | a = b = mid; |
1316 | } |
1317 | } |
1318 | if (cmp != 0) { |
1319 | error(category: errSyntaxError, pos: -1, msg: "Unknown operator '{0:t}' in PostScript function" , tok.get()); |
1320 | return false; |
1321 | } |
1322 | resizeCode(newSize: *codePtr); |
1323 | code[*codePtr].type = psOperator; |
1324 | code[*codePtr].op = (PSOp)a; |
1325 | ++*codePtr; |
1326 | } |
1327 | } |
1328 | return true; |
1329 | } |
1330 | |
1331 | std::unique_ptr<GooString> PostScriptFunction::getToken(Stream *str) |
1332 | { |
1333 | int c; |
1334 | bool ; |
1335 | |
1336 | std::string s; |
1337 | comment = false; |
1338 | while (true) { |
1339 | if ((c = str->getChar()) == EOF) { |
1340 | break; |
1341 | } |
1342 | codeString->append(c); |
1343 | if (comment) { |
1344 | if (c == '\x0a' || c == '\x0d') { |
1345 | comment = false; |
1346 | } |
1347 | } else if (c == '%') { |
1348 | comment = true; |
1349 | } else if (!isspace(c)) { |
1350 | break; |
1351 | } |
1352 | } |
1353 | if (c == '{' || c == '}') { |
1354 | s.push_back(c: (char)c); |
1355 | } else if (isdigit(c) || c == '.' || c == '-') { |
1356 | while (true) { |
1357 | s.push_back(c: (char)c); |
1358 | c = str->lookChar(); |
1359 | if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { |
1360 | break; |
1361 | } |
1362 | str->getChar(); |
1363 | codeString->append(c); |
1364 | } |
1365 | } else { |
1366 | while (true) { |
1367 | s.push_back(c: (char)c); |
1368 | c = str->lookChar(); |
1369 | if (c == EOF || !isalnum(c)) { |
1370 | break; |
1371 | } |
1372 | str->getChar(); |
1373 | codeString->append(c); |
1374 | } |
1375 | } |
1376 | return std::make_unique<GooString>(args&: s); |
1377 | } |
1378 | |
1379 | void PostScriptFunction::resizeCode(int newSize) |
1380 | { |
1381 | if (newSize >= codeSize) { |
1382 | codeSize += 64; |
1383 | code = (PSObject *)greallocn(p: code, count: codeSize, size: sizeof(PSObject)); |
1384 | } |
1385 | } |
1386 | |
1387 | void PostScriptFunction::exec(PSStack *stack, int codePtr) const |
1388 | { |
1389 | int i1, i2; |
1390 | double r1, r2, result; |
1391 | bool b1, b2; |
1392 | |
1393 | while (true) { |
1394 | switch (code[codePtr].type) { |
1395 | case psInt: |
1396 | stack->pushInt(intg: code[codePtr++].intg); |
1397 | break; |
1398 | case psReal: |
1399 | stack->pushReal(real: code[codePtr++].real); |
1400 | break; |
1401 | case psOperator: |
1402 | switch (code[codePtr++].op) { |
1403 | case psOpAbs: |
1404 | if (stack->topIsInt()) { |
1405 | stack->pushInt(intg: abs(x: stack->popInt())); |
1406 | } else { |
1407 | stack->pushReal(real: fabs(x: stack->popNum())); |
1408 | } |
1409 | break; |
1410 | case psOpAdd: |
1411 | if (stack->topTwoAreInts()) { |
1412 | i2 = stack->popInt(); |
1413 | i1 = stack->popInt(); |
1414 | stack->pushInt(intg: i1 + i2); |
1415 | } else { |
1416 | r2 = stack->popNum(); |
1417 | r1 = stack->popNum(); |
1418 | stack->pushReal(real: r1 + r2); |
1419 | } |
1420 | break; |
1421 | case psOpAnd: |
1422 | if (stack->topTwoAreInts()) { |
1423 | i2 = stack->popInt(); |
1424 | i1 = stack->popInt(); |
1425 | stack->pushInt(intg: i1 & i2); |
1426 | } else { |
1427 | b2 = stack->popBool(); |
1428 | b1 = stack->popBool(); |
1429 | stack->pushBool(booln: b1 && b2); |
1430 | } |
1431 | break; |
1432 | case psOpAtan: |
1433 | r2 = stack->popNum(); |
1434 | r1 = stack->popNum(); |
1435 | result = atan2(y: r1, x: r2) * 180.0 / M_PI; |
1436 | if (result < 0) { |
1437 | result += 360.0; |
1438 | } |
1439 | stack->pushReal(real: result); |
1440 | break; |
1441 | case psOpBitshift: |
1442 | i2 = stack->popInt(); |
1443 | i1 = stack->popInt(); |
1444 | if (i2 > 0) { |
1445 | stack->pushInt(intg: i1 << i2); |
1446 | } else if (i2 < 0) { |
1447 | stack->pushInt(intg: (int)((unsigned int)i1 >> -i2)); |
1448 | } else { |
1449 | stack->pushInt(intg: i1); |
1450 | } |
1451 | break; |
1452 | case psOpCeiling: |
1453 | if (!stack->topIsInt()) { |
1454 | stack->pushReal(real: ceil(x: stack->popNum())); |
1455 | } |
1456 | break; |
1457 | case psOpCopy: |
1458 | stack->copy(n: stack->popInt()); |
1459 | break; |
1460 | case psOpCos: |
1461 | stack->pushReal(real: cos(x: stack->popNum() * M_PI / 180.0)); |
1462 | break; |
1463 | case psOpCvi: |
1464 | if (!stack->topIsInt()) { |
1465 | stack->pushInt(intg: (int)stack->popNum()); |
1466 | } |
1467 | break; |
1468 | case psOpCvr: |
1469 | if (!stack->topIsReal()) { |
1470 | stack->pushReal(real: stack->popNum()); |
1471 | } |
1472 | break; |
1473 | case psOpDiv: |
1474 | r2 = stack->popNum(); |
1475 | r1 = stack->popNum(); |
1476 | stack->pushReal(real: r1 / r2); |
1477 | break; |
1478 | case psOpDup: |
1479 | stack->copy(n: 1); |
1480 | break; |
1481 | case psOpEq: |
1482 | if (stack->topTwoAreInts()) { |
1483 | i2 = stack->popInt(); |
1484 | i1 = stack->popInt(); |
1485 | stack->pushBool(booln: i1 == i2); |
1486 | } else if (stack->topTwoAreNums()) { |
1487 | r2 = stack->popNum(); |
1488 | r1 = stack->popNum(); |
1489 | stack->pushBool(booln: r1 == r2); |
1490 | } else { |
1491 | b2 = stack->popBool(); |
1492 | b1 = stack->popBool(); |
1493 | stack->pushBool(booln: b1 == b2); |
1494 | } |
1495 | break; |
1496 | case psOpExch: |
1497 | stack->roll(n: 2, j: 1); |
1498 | break; |
1499 | case psOpExp: |
1500 | r2 = stack->popNum(); |
1501 | r1 = stack->popNum(); |
1502 | stack->pushReal(real: pow(x: r1, y: r2)); |
1503 | break; |
1504 | case psOpFalse: |
1505 | stack->pushBool(booln: false); |
1506 | break; |
1507 | case psOpFloor: |
1508 | if (!stack->topIsInt()) { |
1509 | stack->pushReal(real: floor(x: stack->popNum())); |
1510 | } |
1511 | break; |
1512 | case psOpGe: |
1513 | if (stack->topTwoAreInts()) { |
1514 | i2 = stack->popInt(); |
1515 | i1 = stack->popInt(); |
1516 | stack->pushBool(booln: i1 >= i2); |
1517 | } else { |
1518 | r2 = stack->popNum(); |
1519 | r1 = stack->popNum(); |
1520 | stack->pushBool(booln: r1 >= r2); |
1521 | } |
1522 | break; |
1523 | case psOpGt: |
1524 | if (stack->topTwoAreInts()) { |
1525 | i2 = stack->popInt(); |
1526 | i1 = stack->popInt(); |
1527 | stack->pushBool(booln: i1 > i2); |
1528 | } else { |
1529 | r2 = stack->popNum(); |
1530 | r1 = stack->popNum(); |
1531 | stack->pushBool(booln: r1 > r2); |
1532 | } |
1533 | break; |
1534 | case psOpIdiv: |
1535 | i2 = stack->popInt(); |
1536 | i1 = stack->popInt(); |
1537 | if (likely((i2 != 0) && !(i2 == -1 && i1 == INT_MIN))) { |
1538 | stack->pushInt(intg: i1 / i2); |
1539 | } |
1540 | break; |
1541 | case psOpIndex: |
1542 | stack->index(i: stack->popInt()); |
1543 | break; |
1544 | case psOpLe: |
1545 | if (stack->topTwoAreInts()) { |
1546 | i2 = stack->popInt(); |
1547 | i1 = stack->popInt(); |
1548 | stack->pushBool(booln: i1 <= i2); |
1549 | } else { |
1550 | r2 = stack->popNum(); |
1551 | r1 = stack->popNum(); |
1552 | stack->pushBool(booln: r1 <= r2); |
1553 | } |
1554 | break; |
1555 | case psOpLn: |
1556 | stack->pushReal(real: log(x: stack->popNum())); |
1557 | break; |
1558 | case psOpLog: |
1559 | stack->pushReal(real: log10(x: stack->popNum())); |
1560 | break; |
1561 | case psOpLt: |
1562 | if (stack->topTwoAreInts()) { |
1563 | i2 = stack->popInt(); |
1564 | i1 = stack->popInt(); |
1565 | stack->pushBool(booln: i1 < i2); |
1566 | } else { |
1567 | r2 = stack->popNum(); |
1568 | r1 = stack->popNum(); |
1569 | stack->pushBool(booln: r1 < r2); |
1570 | } |
1571 | break; |
1572 | case psOpMod: |
1573 | i2 = stack->popInt(); |
1574 | i1 = stack->popInt(); |
1575 | if (likely(i2 != 0)) { |
1576 | stack->pushInt(intg: i1 % i2); |
1577 | } |
1578 | break; |
1579 | case psOpMul: |
1580 | if (stack->topTwoAreInts()) { |
1581 | i2 = stack->popInt(); |
1582 | i1 = stack->popInt(); |
1583 | //~ should check for out-of-range, and push a real instead |
1584 | stack->pushInt(intg: i1 * i2); |
1585 | } else { |
1586 | r2 = stack->popNum(); |
1587 | r1 = stack->popNum(); |
1588 | stack->pushReal(real: r1 * r2); |
1589 | } |
1590 | break; |
1591 | case psOpNe: |
1592 | if (stack->topTwoAreInts()) { |
1593 | i2 = stack->popInt(); |
1594 | i1 = stack->popInt(); |
1595 | stack->pushBool(booln: i1 != i2); |
1596 | } else if (stack->topTwoAreNums()) { |
1597 | r2 = stack->popNum(); |
1598 | r1 = stack->popNum(); |
1599 | stack->pushBool(booln: r1 != r2); |
1600 | } else { |
1601 | b2 = stack->popBool(); |
1602 | b1 = stack->popBool(); |
1603 | stack->pushBool(booln: b1 != b2); |
1604 | } |
1605 | break; |
1606 | case psOpNeg: |
1607 | if (stack->topIsInt()) { |
1608 | stack->pushInt(intg: -stack->popInt()); |
1609 | } else { |
1610 | stack->pushReal(real: -stack->popNum()); |
1611 | } |
1612 | break; |
1613 | case psOpNot: |
1614 | if (stack->topIsInt()) { |
1615 | stack->pushInt(intg: ~stack->popInt()); |
1616 | } else { |
1617 | stack->pushBool(booln: !stack->popBool()); |
1618 | } |
1619 | break; |
1620 | case psOpOr: |
1621 | if (stack->topTwoAreInts()) { |
1622 | i2 = stack->popInt(); |
1623 | i1 = stack->popInt(); |
1624 | stack->pushInt(intg: i1 | i2); |
1625 | } else { |
1626 | b2 = stack->popBool(); |
1627 | b1 = stack->popBool(); |
1628 | stack->pushBool(booln: b1 || b2); |
1629 | } |
1630 | break; |
1631 | case psOpPop: |
1632 | stack->pop(); |
1633 | break; |
1634 | case psOpRoll: |
1635 | i2 = stack->popInt(); |
1636 | i1 = stack->popInt(); |
1637 | stack->roll(n: i1, j: i2); |
1638 | break; |
1639 | case psOpRound: |
1640 | if (!stack->topIsInt()) { |
1641 | r1 = stack->popNum(); |
1642 | stack->pushReal(real: (r1 >= 0) ? floor(x: r1 + 0.5) : ceil(x: r1 - 0.5)); |
1643 | } |
1644 | break; |
1645 | case psOpSin: |
1646 | stack->pushReal(real: sin(x: stack->popNum() * M_PI / 180.0)); |
1647 | break; |
1648 | case psOpSqrt: |
1649 | stack->pushReal(real: sqrt(x: stack->popNum())); |
1650 | break; |
1651 | case psOpSub: |
1652 | if (stack->topTwoAreInts()) { |
1653 | i2 = stack->popInt(); |
1654 | i1 = stack->popInt(); |
1655 | stack->pushInt(intg: i1 - i2); |
1656 | } else { |
1657 | r2 = stack->popNum(); |
1658 | r1 = stack->popNum(); |
1659 | stack->pushReal(real: r1 - r2); |
1660 | } |
1661 | break; |
1662 | case psOpTrue: |
1663 | stack->pushBool(booln: true); |
1664 | break; |
1665 | case psOpTruncate: |
1666 | if (!stack->topIsInt()) { |
1667 | r1 = stack->popNum(); |
1668 | stack->pushReal(real: (r1 >= 0) ? floor(x: r1) : ceil(x: r1)); |
1669 | } |
1670 | break; |
1671 | case psOpXor: |
1672 | if (stack->topTwoAreInts()) { |
1673 | i2 = stack->popInt(); |
1674 | i1 = stack->popInt(); |
1675 | stack->pushInt(intg: i1 ^ i2); |
1676 | } else { |
1677 | b2 = stack->popBool(); |
1678 | b1 = stack->popBool(); |
1679 | stack->pushBool(booln: b1 ^ b2); |
1680 | } |
1681 | break; |
1682 | case psOpIf: |
1683 | b1 = stack->popBool(); |
1684 | if (b1) { |
1685 | exec(stack, codePtr: codePtr + 2); |
1686 | } |
1687 | codePtr = code[codePtr + 1].blk; |
1688 | break; |
1689 | case psOpIfelse: |
1690 | b1 = stack->popBool(); |
1691 | if (b1) { |
1692 | exec(stack, codePtr: codePtr + 2); |
1693 | } else { |
1694 | exec(stack, codePtr: code[codePtr].blk); |
1695 | } |
1696 | codePtr = code[codePtr + 1].blk; |
1697 | break; |
1698 | case psOpReturn: |
1699 | return; |
1700 | } |
1701 | break; |
1702 | default: |
1703 | error(category: errSyntaxError, pos: -1, msg: "Internal: bad object in PostScript function code" ); |
1704 | break; |
1705 | } |
1706 | } |
1707 | } |
1708 | |