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