/src/ghostpdl/devices/vector/gdevpsds.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Image processing streams for PostScript and PDF writers */ |
18 | | #include "gx.h" |
19 | | #include "memory_.h" |
20 | | #include "gserrors.h" |
21 | | #include "gxdcconv.h" |
22 | | #include "gdevpsds.h" |
23 | | #include "gxbitmap.h" |
24 | | #include "gxcspace.h" |
25 | | #include "gsdcolor.h" |
26 | | #include "gscspace.h" |
27 | | #include "gxdevcli.h" |
28 | | #include "gxgstate.h" |
29 | | #include "gsicc_manage.h" |
30 | | |
31 | | /* ---------------- Convert between 1/2/4/12 and 8 bits ---------------- */ |
32 | | |
33 | | gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state"); |
34 | | |
35 | | /* Initialize an expansion or reduction stream. */ |
36 | | int |
37 | | s_1248_init(stream_1248_state *ss, int Columns, int samples_per_pixel) |
38 | 0 | { |
39 | 0 | ss->samples_per_row = Columns * samples_per_pixel; |
40 | 0 | return ss->templat->init((stream_state *)ss); |
41 | 0 | } |
42 | | |
43 | | /* Initialize the state. */ |
44 | | static int |
45 | | s_1_init(stream_state * st) |
46 | 0 | { |
47 | 0 | stream_1248_state *const ss = (stream_1248_state *) st; |
48 | |
|
49 | 0 | ss->left = ss->samples_per_row; |
50 | 0 | ss->bits_per_sample = 1; |
51 | 0 | return 0; |
52 | 0 | } |
53 | | static int |
54 | | s_2_init(stream_state * st) |
55 | 0 | { |
56 | 0 | stream_1248_state *const ss = (stream_1248_state *) st; |
57 | |
|
58 | 0 | ss->left = ss->samples_per_row; |
59 | 0 | ss->bits_per_sample = 2; |
60 | 0 | return 0; |
61 | 0 | } |
62 | | static int |
63 | | s_4_init(stream_state * st) |
64 | 0 | { |
65 | 0 | stream_1248_state *const ss = (stream_1248_state *) st; |
66 | |
|
67 | 0 | ss->left = ss->samples_per_row; |
68 | 0 | ss->bits_per_sample = 4; |
69 | 0 | return 0; |
70 | 0 | } |
71 | | static int |
72 | | s_12_init(stream_state * st) |
73 | 0 | { |
74 | 0 | stream_1248_state *const ss = (stream_1248_state *) st; |
75 | |
|
76 | 0 | ss->left = ss->samples_per_row; |
77 | 0 | ss->bits_per_sample = 12; /* not needed */ |
78 | 0 | return 0; |
79 | 0 | } |
80 | | static int |
81 | | s_16_init(stream_state * st) |
82 | 0 | { |
83 | 0 | stream_1248_state *const ss = (stream_1248_state *) st; |
84 | |
|
85 | 0 | ss->left = ss->samples_per_row; |
86 | 0 | ss->bits_per_sample = 16; /* not needed */ |
87 | 0 | return 0; |
88 | 0 | } |
89 | | |
90 | | /* Process one buffer. */ |
91 | | #define BEGIN_1248\ |
92 | 0 | stream_1248_state * const ss = (stream_1248_state *)st;\ |
93 | 0 | const byte *p = pr->ptr;\ |
94 | 0 | const byte *rlimit = pr->limit;\ |
95 | 0 | byte *q = pw->ptr;\ |
96 | 0 | byte *wlimit = pw->limit;\ |
97 | 0 | uint left = ss->left;\ |
98 | 0 | int status;\ |
99 | 0 | int n |
100 | | #define END_1248\ |
101 | 0 | pr->ptr = p;\ |
102 | 0 | pw->ptr = q;\ |
103 | 0 | ss->left = left;\ |
104 | 0 | return status |
105 | | |
106 | | /* N-to-8 expansion */ |
107 | | #define FOREACH_N_8(in, nout)\ |
108 | 0 | status = 0;\ |
109 | 0 | for ( ; p < rlimit; left -= n, q += n, ++p ) {\ |
110 | 0 | byte in = p[1];\ |
111 | 0 | n = min(left, nout);\ |
112 | 0 | if ( wlimit - q < n ) {\ |
113 | 0 | status = 1;\ |
114 | 0 | break;\ |
115 | 0 | }\ |
116 | 0 | switch ( n ) {\ |
117 | 0 | case 0: left = ss->samples_per_row; --p; continue; |
118 | | #define END_FOREACH_N_8\ |
119 | 0 | }\ |
120 | 0 | } |
121 | | static int |
122 | | s_N_8_process(stream_state * st, stream_cursor_read * pr, |
123 | | stream_cursor_write * pw, bool last) |
124 | 0 | { |
125 | 0 | BEGIN_1248; |
126 | |
|
127 | 0 | switch (ss->bits_per_sample) { |
128 | | |
129 | 0 | case 1:{ |
130 | 0 | FOREACH_N_8(in, 8) |
131 | 0 | case 8: |
132 | 0 | q[8] = (byte) - (in & 1); |
133 | 0 | case 7: |
134 | 0 | q[7] = (byte) - ((in >> 1) & 1); |
135 | 0 | case 6: |
136 | 0 | q[6] = (byte) - ((in >> 2) & 1); |
137 | 0 | case 5: |
138 | 0 | q[5] = (byte) - ((in >> 3) & 1); |
139 | 0 | case 4: |
140 | 0 | q[4] = (byte) - ((in >> 4) & 1); |
141 | 0 | case 3: |
142 | 0 | q[3] = (byte) - ((in >> 5) & 1); |
143 | 0 | case 2: |
144 | 0 | q[2] = (byte) - ((in >> 6) & 1); |
145 | 0 | case 1: |
146 | 0 | q[1] = (byte) - (in >> 7); |
147 | 0 | END_FOREACH_N_8; |
148 | 0 | } |
149 | 0 | break; |
150 | | |
151 | 0 | case 2:{ |
152 | 0 | static const byte b2[4] = |
153 | 0 | {0x00, 0x55, 0xaa, 0xff}; |
154 | |
|
155 | 0 | FOREACH_N_8(in, 4) |
156 | 0 | case 4: |
157 | 0 | q[4] = b2[in & 3]; |
158 | 0 | case 3: |
159 | 0 | q[3] = b2[(in >> 2) & 3]; |
160 | 0 | case 2: |
161 | 0 | q[2] = b2[(in >> 4) & 3]; |
162 | 0 | case 1: |
163 | 0 | q[1] = b2[in >> 6]; |
164 | 0 | END_FOREACH_N_8; |
165 | 0 | } |
166 | 0 | break; |
167 | | |
168 | 0 | case 4:{ |
169 | 0 | static const byte b4[16] = |
170 | 0 | { |
171 | 0 | 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
172 | 0 | 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff |
173 | 0 | }; |
174 | |
|
175 | 0 | FOREACH_N_8(in, 2) |
176 | 0 | case 2: |
177 | 0 | q[2] = b4[in & 0xf]; |
178 | 0 | case 1: |
179 | 0 | q[1] = b4[in >> 4]; |
180 | 0 | END_FOREACH_N_8; |
181 | 0 | } |
182 | 0 | break; |
183 | | |
184 | 0 | default: |
185 | 0 | return ERRC; |
186 | 0 | } |
187 | | |
188 | 0 | END_1248; |
189 | 0 | } |
190 | | |
191 | | /* 12-to-8 "expansion" */ |
192 | | static int |
193 | | s_12_8_process(stream_state * st, stream_cursor_read * pr, |
194 | | stream_cursor_write * pw, bool last) |
195 | 0 | { |
196 | 0 | BEGIN_1248; |
197 | |
|
198 | 0 | n = ss->samples_per_row; /* misuse n to avoid a compiler warning */ |
199 | 0 | status = 0; |
200 | 0 | for (; rlimit - p >= 2; ++q) { |
201 | 0 | if (q >= wlimit) { |
202 | 0 | status = 1; |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | if (left == 0) |
206 | 0 | left = n; |
207 | 0 | if ((n - left) & 1) { |
208 | 0 | q[1] = (byte)((p[1] << 4) | (p[2] >> 4)); |
209 | 0 | p += 2, --left; |
210 | 0 | } else { |
211 | 0 | q[1] = *++p; |
212 | 0 | if (!--left) |
213 | 0 | ++p; |
214 | 0 | } |
215 | 0 | } |
216 | |
|
217 | 0 | END_1248; |
218 | 0 | } |
219 | | |
220 | | /* 16-to-8 "expansion" */ |
221 | | static int |
222 | | s_16_8_process(stream_state * st, stream_cursor_read * pr, |
223 | | stream_cursor_write * pw, bool last) |
224 | 0 | { |
225 | 0 | BEGIN_1248; |
226 | |
|
227 | 0 | (void)n; /* misuse n to avoid a compiler warning */ |
228 | 0 | (void)ss; |
229 | 0 | status = 0; |
230 | 0 | for (; rlimit - p >= 2; ++q) { |
231 | 0 | if (q >= wlimit) { |
232 | 0 | status = 1; |
233 | 0 | break; |
234 | 0 | } |
235 | 0 | q[1] = (byte)p[1]; /* Set output to the high byte of the input */ |
236 | 0 | p+=2; /* Discard the low byte */ |
237 | 0 | } |
238 | 0 | END_1248; |
239 | 0 | } |
240 | | |
241 | | /* 8-to-N reduction */ |
242 | | #define FOREACH_8_N(out, nin)\ |
243 | 0 | byte out;\ |
244 | 0 | status = 1;\ |
245 | 0 | for ( ; q < wlimit; left -= n, p += n, ++q ) {\ |
246 | 0 | n = min(left, nin);\ |
247 | 0 | if ( rlimit - p < n ) {\ |
248 | 0 | status = 0;\ |
249 | 0 | break;\ |
250 | 0 | }\ |
251 | 0 | out = 0;\ |
252 | 0 | switch ( n ) {\ |
253 | 0 | case 0: left = ss->samples_per_row; --q; continue; |
254 | | #define END_FOREACH_8_N\ |
255 | 0 | q[1] = out;\ |
256 | 0 | }\ |
257 | 0 | } |
258 | | static int |
259 | | s_8_N_process(stream_state * st, stream_cursor_read * pr, |
260 | | stream_cursor_write * pw, bool last) |
261 | 0 | { |
262 | 0 | BEGIN_1248; |
263 | |
|
264 | 0 | switch (ss->bits_per_sample) { |
265 | | |
266 | 0 | case 1:{ |
267 | 0 | FOREACH_8_N(out, 8) |
268 | 0 | case 8: |
269 | 0 | out = p[8] >> 7; |
270 | 0 | case 7: |
271 | 0 | out |= (p[7] >> 7) << 1; |
272 | 0 | case 6: |
273 | 0 | out |= (p[6] >> 7) << 2; |
274 | 0 | case 5: |
275 | 0 | out |= (p[5] >> 7) << 3; |
276 | 0 | case 4: |
277 | 0 | out |= (p[4] >> 7) << 4; |
278 | 0 | case 3: |
279 | 0 | out |= (p[3] >> 7) << 5; |
280 | 0 | case 2: |
281 | 0 | out |= (p[2] >> 7) << 6; |
282 | 0 | case 1: |
283 | 0 | out |= p[1] & 0x80; |
284 | 0 | END_FOREACH_8_N; |
285 | 0 | } |
286 | 0 | break; |
287 | | |
288 | 0 | case 2:{ |
289 | 0 | FOREACH_8_N(out, 4) |
290 | 0 | case 4: |
291 | 0 | out |= p[4] >> 6; |
292 | 0 | case 3: |
293 | 0 | out |= (p[3] >> 6) << 2; |
294 | 0 | case 2: |
295 | 0 | out |= (p[2] >> 6) << 4; |
296 | 0 | case 1: |
297 | 0 | out |= p[1] & 0xc0; |
298 | 0 | END_FOREACH_8_N; |
299 | 0 | } |
300 | 0 | break; |
301 | | |
302 | 0 | case 4:{ |
303 | 0 | FOREACH_8_N(out, 2) |
304 | 0 | case 2: |
305 | 0 | out |= p[2] >> 4; |
306 | 0 | case 1: |
307 | 0 | out |= p[1] & 0xf0; |
308 | 0 | END_FOREACH_8_N; |
309 | 0 | } |
310 | 0 | break; |
311 | | |
312 | 0 | default: |
313 | 0 | return ERRC; |
314 | 0 | } |
315 | | |
316 | 0 | END_1248; |
317 | 0 | } |
318 | | |
319 | | const stream_template s_1_8_template = { |
320 | | &st_1248_state, s_1_init, s_N_8_process, 1, 8 |
321 | | }; |
322 | | const stream_template s_2_8_template = { |
323 | | &st_1248_state, s_2_init, s_N_8_process, 1, 4 |
324 | | }; |
325 | | const stream_template s_4_8_template = { |
326 | | &st_1248_state, s_4_init, s_N_8_process, 1, 2 |
327 | | }; |
328 | | const stream_template s_12_8_template = { |
329 | | &st_1248_state, s_12_init, s_12_8_process, 1, 2 |
330 | | }; |
331 | | const stream_template s_16_8_template = { |
332 | | &st_1248_state, s_16_init, s_16_8_process, 1, 2 |
333 | | }; |
334 | | |
335 | | const stream_template s_8_1_template = { |
336 | | &st_1248_state, s_1_init, s_8_N_process, 8, 1 |
337 | | }; |
338 | | const stream_template s_8_2_template = { |
339 | | &st_1248_state, s_2_init, s_8_N_process, 4, 1 |
340 | | }; |
341 | | const stream_template s_8_4_template = { |
342 | | &st_1248_state, s_4_init, s_8_N_process, 2, 1 |
343 | | }; |
344 | | |
345 | | /* ---------------- Color space conversion ---------------- */ |
346 | | |
347 | | /* ------ Convert CMYK to RGB ------ */ |
348 | | |
349 | | private_st_C2R_state(); |
350 | | |
351 | | /* Initialize a CMYK => RGB conversion stream. */ |
352 | | int |
353 | | s_C2R_init(stream_C2R_state *ss, const gs_gstate *pgs) |
354 | 0 | { |
355 | 0 | ss->pgs = pgs; |
356 | 0 | return 0; |
357 | 0 | } |
358 | | |
359 | | /* Set default parameter values (actually, just clear pointers). */ |
360 | | static void |
361 | | s_C2R_set_defaults(stream_state * st) |
362 | 0 | { |
363 | 0 | stream_C2R_state *const ss = (stream_C2R_state *) st; |
364 | |
|
365 | 0 | ss->pgs = 0; |
366 | 0 | } |
367 | | |
368 | | /* Process one buffer. */ |
369 | | static int |
370 | | s_C2R_process(stream_state * st, stream_cursor_read * pr, |
371 | | stream_cursor_write * pw, bool last) |
372 | 0 | { |
373 | 0 | stream_C2R_state *const ss = (stream_C2R_state *) st; |
374 | 0 | const byte *p = pr->ptr; |
375 | 0 | const byte *rlimit = pr->limit; |
376 | 0 | byte *q = pw->ptr; |
377 | 0 | byte *wlimit = pw->limit; |
378 | |
|
379 | 0 | for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) { |
380 | 0 | byte bc = p[1], bm = p[2], by = p[3], bk = p[4]; |
381 | 0 | frac rgb[3]; |
382 | |
|
383 | 0 | color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by), |
384 | 0 | byte2frac(bk), ss->pgs, rgb, ss->pgs->memory); |
385 | 0 | q[1] = frac2byte(rgb[0]); |
386 | 0 | q[2] = frac2byte(rgb[1]); |
387 | 0 | q[3] = frac2byte(rgb[2]); |
388 | 0 | } |
389 | 0 | pr->ptr = p; |
390 | 0 | pw->ptr = q; |
391 | 0 | return (rlimit - p < 4 ? 0 : 1); |
392 | 0 | } |
393 | | |
394 | | const stream_template s_C2R_template = { |
395 | | &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3, 0, s_C2R_set_defaults |
396 | | }; |
397 | | |
398 | | /* ------ Convert any color space to Indexed ------ */ |
399 | | |
400 | | private_st_IE_state(); |
401 | | static |
402 | 0 | ENUM_PTRS_WITH(ie_state_enum_ptrs, stream_IE_state *st) return 0; |
403 | 0 | case 0: return ENUM_OBJ(st->Decode); |
404 | 0 | case 1: return ENUM_BYTESTRING(&st->Table); |
405 | 0 | ENUM_PTRS_END |
406 | | static |
407 | 0 | RELOC_PTRS_WITH(ie_state_reloc_ptrs, stream_IE_state *st) |
408 | 0 | { |
409 | 0 | RELOC_VAR(st->Decode); |
410 | 0 | RELOC_BYTESTRING_VAR(st->Table); |
411 | 0 | } |
412 | 0 | RELOC_PTRS_END |
413 | | |
414 | | /* Set defaults. */ |
415 | | static void |
416 | | s_IE_set_defaults(stream_state * st) |
417 | 0 | { |
418 | 0 | stream_IE_state *const ss = (stream_IE_state *) st; |
419 | |
|
420 | 0 | ss->Decode = 0; /* clear pointers */ |
421 | 0 | gs_bytestring_from_string(&ss->Table, 0, 0); |
422 | 0 | } |
423 | | |
424 | | /* Initialize the state. */ |
425 | | static int |
426 | | s_IE_init(stream_state * st) |
427 | 0 | { |
428 | 0 | stream_IE_state *const ss = (stream_IE_state *) st; |
429 | 0 | int key_index = (1 << ss->BitsPerIndex) * ss->NumComponents; |
430 | 0 | int i; |
431 | |
|
432 | 0 | if (ss->Table.data == 0 || ss->Table.size < key_index) |
433 | 0 | return ERRC; /****** WRONG ******/ |
434 | | /* Initialize Table with default values. */ |
435 | 0 | memset(ss->Table.data, 0, ss->NumComponents); |
436 | 0 | ss->Table.data[ss->Table.size - 1] = 0; |
437 | 0 | for (i = 0; i < countof(ss->hash_table); ++i) |
438 | 0 | ss->hash_table[i] = key_index; |
439 | 0 | ss->next_index = 0; |
440 | 0 | ss->in_bits_left = 0; |
441 | 0 | ss->next_component = 0; |
442 | 0 | ss->byte_out = 1; |
443 | 0 | ss->x = 0; |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | /* Process a buffer. */ |
448 | | static int |
449 | | s_IE_process(stream_state * st, stream_cursor_read * pr, |
450 | | stream_cursor_write * pw, bool last) |
451 | 0 | { |
452 | 0 | stream_IE_state *const ss = (stream_IE_state *) st; |
453 | | /* Constant values from the state */ |
454 | 0 | const int bpc = ss->BitsPerComponent; |
455 | 0 | const int num_components = ss->NumComponents; |
456 | 0 | const int end_index = (1 << ss->BitsPerIndex) * num_components; |
457 | 0 | byte *const table = ss->Table.data; |
458 | 0 | byte *const key = table + end_index; |
459 | | /* Dynamic values from the state */ |
460 | 0 | uint byte_in = ss->byte_in; |
461 | 0 | int in_bits_left = ss->in_bits_left; |
462 | 0 | int next_component = ss->next_component; |
463 | 0 | uint byte_out = ss->byte_out; |
464 | | /* Other dynamic values */ |
465 | 0 | const byte *p = pr->ptr; |
466 | 0 | const byte *rlimit = pr->limit; |
467 | 0 | byte *q = pw->ptr; |
468 | 0 | byte *wlimit = pw->limit; |
469 | 0 | int status = 0; |
470 | |
|
471 | 0 | for (;;) { |
472 | 0 | uint hash, reprobe; |
473 | 0 | int i, index; |
474 | | |
475 | | /* Check for a filled output byte. */ |
476 | 0 | if (byte_out >= 0x100) { |
477 | 0 | if (q >= wlimit) { |
478 | 0 | status = 1; |
479 | 0 | break; |
480 | 0 | } |
481 | 0 | *++q = (byte)byte_out; |
482 | 0 | byte_out = 1; |
483 | 0 | } |
484 | | /* Acquire a complete input value. */ |
485 | 0 | while (next_component < num_components) { |
486 | 0 | const float *decode = &ss->Decode[next_component * 2]; |
487 | 0 | int sample; |
488 | |
|
489 | 0 | if (in_bits_left == 0) { |
490 | 0 | if (p >= rlimit) |
491 | 0 | goto out; |
492 | 0 | byte_in = *++p; |
493 | 0 | in_bits_left = 8; |
494 | 0 | } |
495 | | /* An input sample can never span a byte boundary. */ |
496 | 0 | in_bits_left -= bpc; |
497 | 0 | sample = (byte_in >> in_bits_left) & ((1 << bpc) - 1); |
498 | | /* Scale the sample according to Decode. */ |
499 | 0 | sample = (int)((decode[0] + |
500 | 0 | (sample / (float)((1 << bpc) - 1) * |
501 | 0 | (decode[1] - decode[0]))) * 255 + 0.5); |
502 | 0 | key[next_component++] = |
503 | 0 | (sample < 0 ? 0 : sample > 255 ? 255 : (byte)sample); |
504 | 0 | } |
505 | | /* Look up the input value. */ |
506 | 0 | for (hash = 0, i = 0; i < num_components; ++i) |
507 | 0 | hash = hash + 23 * key[i]; /* adhoc */ |
508 | 0 | reprobe = (hash / countof(ss->hash_table)) | 137; /* adhoc */ |
509 | 0 | for (hash %= countof(ss->hash_table); |
510 | 0 | memcmp(table + ss->hash_table[hash], key, num_components); |
511 | 0 | hash = (hash + reprobe) % countof(ss->hash_table) |
512 | 0 | ) |
513 | 0 | DO_NOTHING; |
514 | 0 | index = ss->hash_table[hash]; |
515 | 0 | if (index == end_index) { |
516 | | /* The match was on an empty entry. */ |
517 | 0 | if (ss->next_index == end_index) { |
518 | | /* Too many different values. */ |
519 | 0 | status = ERRC; |
520 | 0 | break; |
521 | 0 | } |
522 | 0 | ss->hash_table[hash] = index = ss->next_index; |
523 | 0 | ss->next_index += num_components; |
524 | 0 | memcpy(table + index, key, num_components); |
525 | 0 | } |
526 | 0 | byte_out = (byte_out << ss->BitsPerIndex) + index / num_components; |
527 | 0 | next_component = 0; |
528 | 0 | if (++(ss->x) == ss->Width) { |
529 | | /* Handle input and output padding. */ |
530 | 0 | in_bits_left = 0; |
531 | 0 | if (byte_out != 1) |
532 | 0 | while (byte_out < 0x100) |
533 | 0 | byte_out <<= 1; |
534 | 0 | ss->x = 0; |
535 | 0 | } |
536 | 0 | } |
537 | 0 | out: |
538 | 0 | pr->ptr = p; |
539 | 0 | pw->ptr = q; |
540 | 0 | ss->byte_in = byte_in; |
541 | 0 | ss->in_bits_left = in_bits_left; |
542 | 0 | ss->next_component = next_component; |
543 | 0 | ss->byte_out = byte_out; |
544 | | /* For simplicity, always update the record of the table size. */ |
545 | 0 | ss->Table.data[ss->Table.size - 1] = |
546 | 0 | (ss->next_index == 0 ? 0 : |
547 | 0 | ss->next_index / ss->NumComponents - 1); |
548 | 0 | return status; |
549 | 0 | } |
550 | | |
551 | | const stream_template s_IE_template = { |
552 | | &st_IE_state, s_IE_init, s_IE_process, 1, 1, |
553 | | 0 /* NULL */, s_IE_set_defaults |
554 | | }; |
555 | | |
556 | | /* ---------------- Downsampling ---------------- */ |
557 | | |
558 | | /* Return the number of samples after downsampling. */ |
559 | | int |
560 | | s_Downsample_size_out(int size_in, float factor, bool pad) |
561 | 0 | { |
562 | 0 | return (int)((pad ? size_in + factor - 1 : size_in) / factor); |
563 | 0 | } |
564 | | |
565 | | static void |
566 | | s_Downsample_set_defaults(register stream_state * st) |
567 | 0 | { |
568 | 0 | stream_Downsample_state *const ss = (stream_Downsample_state *)st; |
569 | |
|
570 | 0 | s_Downsample_set_defaults_inline(ss); |
571 | 0 | } |
572 | | |
573 | | static int |
574 | | s_Downsample_init_common(stream_state * st) |
575 | 0 | { |
576 | 0 | stream_Downsample_state *const ss = (stream_Downsample_state *) st; |
577 | 0 | ss->x = ss->y = 0; |
578 | 0 | return 0; |
579 | 0 | } |
580 | | |
581 | | /* ------ Subsample ------ */ |
582 | | |
583 | | gs_private_st_simple(st_Subsample_state, stream_Subsample_state, |
584 | | "stream_Subsample_state"); |
585 | | |
586 | | /* Initialize the state. */ |
587 | | static int |
588 | | s_Subsample_init(stream_state * st) |
589 | 0 | { |
590 | 0 | stream_Subsample_state *const ss = (stream_Subsample_state *) st; |
591 | 0 | int xf = (int)ss->XFactor; |
592 | |
|
593 | 0 | if ((float)xf != ss->XFactor) { |
594 | 0 | dmprintf1(st->memory, |
595 | 0 | "Subsample filter does not support non-integer downsample factor (%f)\n", |
596 | 0 | ss->XFactor); |
597 | 0 | return ERRC; |
598 | 0 | } |
599 | 0 | return s_Downsample_init_common(st); |
600 | 0 | } |
601 | | |
602 | | /* Process one buffer. */ |
603 | | static int |
604 | | s_Subsample_process(stream_state * st, stream_cursor_read * pr, |
605 | | stream_cursor_write * pw, bool last) |
606 | 0 | { |
607 | 0 | stream_Subsample_state *const ss = (stream_Subsample_state *) st; |
608 | 0 | const byte *p = pr->ptr; |
609 | 0 | const byte *rlimit = pr->limit; |
610 | 0 | byte *q = pw->ptr; |
611 | 0 | byte *wlimit = pw->limit; |
612 | 0 | int spp = ss->Colors; |
613 | 0 | int width = ss->WidthIn, height = ss->HeightIn; |
614 | 0 | int xf = (int)ss->XFactor, yf = (int)ss->YFactor; |
615 | 0 | int xf2 = xf / 2, yf2 = yf / 2; |
616 | 0 | int xlimit = (width / xf) * xf, ylimit = (height / yf) * yf; |
617 | 0 | int xlast = |
618 | 0 | (ss->padX && xlimit < width ? xlimit + (width % xf) / 2 : -1); |
619 | 0 | int ylast = |
620 | 0 | (ss->padY && ylimit < height ? ylimit + (height % yf) / 2 : -1); |
621 | 0 | int x = ss->x, y = ss->y; |
622 | 0 | int status = 0; |
623 | |
|
624 | 0 | if_debug4m('w', st->memory, |
625 | 0 | "[w]subsample: x=%d, y=%d, rcount=%ld, wcount=%ld\n", |
626 | 0 | x, y, (long)(rlimit - p), (long)(wlimit - q)); |
627 | 0 | for (; rlimit - p >= spp; p += spp) { |
628 | 0 | if (((y % yf == yf2 && y < ylimit) || y == ylast) && |
629 | 0 | ((x % xf == xf2 && x < xlimit) || x == xlast) |
630 | 0 | ) { |
631 | 0 | if (wlimit - q < spp) { |
632 | 0 | status = 1; |
633 | 0 | break; |
634 | 0 | } |
635 | 0 | memcpy(q + 1, p + 1, spp); |
636 | 0 | q += spp; |
637 | 0 | } |
638 | 0 | if (++x == width) |
639 | 0 | x = 0, ++y; |
640 | 0 | } |
641 | 0 | if_debug5m('w', st->memory, |
642 | 0 | "[w]subsample: x'=%d, y'=%d, read %ld, wrote %ld, status = %d\n", |
643 | 0 | x, y, (long)(p - pr->ptr), (long)(q - pw->ptr), status); |
644 | 0 | pr->ptr = p; |
645 | 0 | pw->ptr = q; |
646 | 0 | ss->x = x, ss->y = y; |
647 | 0 | return status; |
648 | 0 | } |
649 | | |
650 | | const stream_template s_Subsample_template = { |
651 | | &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4, |
652 | | 0 /* NULL */, s_Downsample_set_defaults |
653 | | }; |
654 | | |
655 | | /* ------ Average ------ */ |
656 | | |
657 | | private_st_Average_state(); |
658 | | |
659 | | /* Set default parameter values (actually, just clear pointers). */ |
660 | | static void |
661 | | s_Average_set_defaults(stream_state * st) |
662 | 0 | { |
663 | 0 | stream_Average_state *const ss = (stream_Average_state *) st; |
664 | |
|
665 | 0 | s_Downsample_set_defaults(st); |
666 | | /* Clear pointers */ |
667 | 0 | ss->sums = 0; |
668 | 0 | } |
669 | | |
670 | | /* Initialize the state. */ |
671 | | static int |
672 | | s_Average_init(stream_state * st) |
673 | 0 | { |
674 | 0 | stream_Average_state *const ss = (stream_Average_state *) st; |
675 | 0 | int xf = (int)ss->XFactor; |
676 | |
|
677 | 0 | if ((float)xf != ss->XFactor) { |
678 | 0 | dmprintf1(st->memory, |
679 | 0 | "Average filter does not support non-integer downsample factor (%f)\n", |
680 | 0 | ss->XFactor); |
681 | 0 | return ERRC; |
682 | 0 | } |
683 | | |
684 | 0 | ss->sum_size = |
685 | 0 | ss->Colors * ((ss->WidthIn + xf - 1) / xf); |
686 | 0 | ss->copy_size = ss->sum_size - |
687 | 0 | (ss->padX || (ss->WidthIn % xf == 0) ? 0 : ss->Colors); |
688 | 0 | if (ss->sums) |
689 | 0 | gs_free_object(st->memory, ss->sums, "Average sums"); |
690 | 0 | ss->sums = |
691 | 0 | (uint *)gs_alloc_byte_array(st->memory, ss->sum_size, |
692 | 0 | sizeof(uint), "Average sums"); |
693 | 0 | if (ss->sums == 0) |
694 | 0 | return ERRC; /****** WRONG ******/ |
695 | 0 | memset(ss->sums, 0, ss->sum_size * sizeof(uint)); |
696 | 0 | return s_Downsample_init_common(st); |
697 | 0 | } |
698 | | |
699 | | /* Release the state. */ |
700 | | static void |
701 | | s_Average_release(stream_state * st) |
702 | 0 | { |
703 | 0 | stream_Average_state *const ss = (stream_Average_state *) st; |
704 | |
|
705 | 0 | gs_free_object(st->memory, ss->sums, "Average sums"); |
706 | 0 | } |
707 | | |
708 | | /* Process one buffer. */ |
709 | | static int |
710 | | s_Average_process(stream_state * st, stream_cursor_read * pr, |
711 | | stream_cursor_write * pw, bool last) |
712 | 0 | { |
713 | 0 | stream_Average_state *const ss = (stream_Average_state *) st; |
714 | 0 | const byte *p = pr->ptr; |
715 | 0 | const byte *rlimit = pr->limit; |
716 | 0 | byte *q = pw->ptr; |
717 | 0 | byte *wlimit = pw->limit; |
718 | 0 | int spp = ss->Colors; |
719 | 0 | int width = ss->WidthIn; |
720 | 0 | int xf = (int)ss->XFactor, yf = (int)ss->YFactor; |
721 | 0 | int x = ss->x, y = ss->y; |
722 | 0 | uint *sums = ss->sums; |
723 | 0 | int status = 0; |
724 | |
|
725 | 0 | top: |
726 | 0 | if (y == yf || (last && p >= rlimit && ss->padY && y != 0)) { |
727 | | /* We're copying averaged values to the output. */ |
728 | 0 | int ncopy = min(ss->copy_size - x, wlimit - q); |
729 | |
|
730 | 0 | if (ncopy) { |
731 | 0 | int scale = xf * y; |
732 | |
|
733 | 0 | while (--ncopy >= 0) |
734 | 0 | *++q = (byte) (sums[x++] / scale); |
735 | 0 | } |
736 | 0 | if (x < ss->copy_size) { |
737 | 0 | status = 1; |
738 | 0 | goto out; |
739 | 0 | } |
740 | | /* Done copying. */ |
741 | 0 | x = y = 0; |
742 | 0 | memset(sums, 0, ss->sum_size * sizeof(uint)); |
743 | 0 | } |
744 | 0 | while (rlimit - p >= spp) { |
745 | 0 | uint *bp = sums + x / xf * spp; |
746 | 0 | int i; |
747 | |
|
748 | 0 | for (i = spp; --i >= 0;) |
749 | 0 | *bp++ += *++p; |
750 | 0 | if (++x == width) { |
751 | 0 | x = 0; |
752 | 0 | ++y; |
753 | 0 | goto top; |
754 | 0 | } |
755 | 0 | } |
756 | 0 | out: |
757 | 0 | pr->ptr = p; |
758 | 0 | pw->ptr = q; |
759 | 0 | ss->x = x, ss->y = y; |
760 | 0 | return status; |
761 | 0 | } |
762 | | |
763 | | const stream_template s_Average_template = { |
764 | | &st_Average_state, s_Average_init, s_Average_process, 4, 4, |
765 | | s_Average_release, s_Average_set_defaults |
766 | | }; |
767 | | |
768 | | /* ------ Bicubic ------ */ |
769 | | |
770 | | private_st_Bicubic_state(); |
771 | | |
772 | | /* Set default parameter values (actually, just clear pointers). */ |
773 | | static void |
774 | | s_Bicubic_set_defaults(stream_state * st) |
775 | 0 | { |
776 | 0 | stream_Bicubic_state *const ss = (stream_Bicubic_state *) st; |
777 | |
|
778 | 0 | s_Downsample_set_defaults(st); |
779 | |
|
780 | 0 | ss->data = NULL; |
781 | 0 | } |
782 | | |
783 | | /* Initialize the state. */ |
784 | | static int |
785 | | s_Bicubic_init(stream_state * st) |
786 | 0 | { |
787 | 0 | stream_Bicubic_state *const ss = (stream_Bicubic_state *) st; |
788 | |
|
789 | 0 | if (ss->WidthIn < 4 || ss->HeightIn < 4) |
790 | 0 | return ERRC; |
791 | | |
792 | | /* bicubic interpolation requires 4 lines of data */ |
793 | | |
794 | 0 | ss->l_size = (ss->WidthIn * ss->Colors); |
795 | 0 | ss->d_size = (ss->l_size * 4); |
796 | 0 | ss->d_len = 0; |
797 | 0 | ss->y_in = 0; |
798 | |
|
799 | 0 | if (ss->data) |
800 | 0 | gs_free_object(st->memory, ss->data, "Bicubic data"); |
801 | 0 | ss->data = (byte *)gs_alloc_bytes(st->memory, ss->d_size, "Bicubic data"); |
802 | 0 | if (ss->data == NULL) |
803 | 0 | return ERRC; /****** WRONG ******/ |
804 | | |
805 | 0 | return s_Downsample_init_common(st); |
806 | 0 | } |
807 | | |
808 | | /* Release the state. */ |
809 | | static void |
810 | | s_Bicubic_release(stream_state * st) |
811 | 0 | { |
812 | 0 | stream_Bicubic_state *const ss = (stream_Bicubic_state *) st; |
813 | |
|
814 | 0 | gs_free_object(st->memory, ss->data, "Bicubic data"); |
815 | 0 | } |
816 | | |
817 | | static inline byte |
818 | | s_Bicubic_data_at(stream_Bicubic_state *const ss, int x, int y, int c) |
819 | 0 | { |
820 | 0 | ulong idx; |
821 | 0 | if (y >= ss->HeightIn) |
822 | 0 | y = ss->HeightIn - 1; |
823 | 0 | y -= ss->y_in; |
824 | 0 | idx = ss->l_size * (y < 0 ? 0 : y) + |
825 | 0 | (size_t)(x < 0 ? 0 : x >= ss->WidthIn ? ss->WidthIn-1 : x) * |
826 | 0 | ss->Colors + c; |
827 | 0 | return (idx < ss->d_len) ? ss->data[idx] : 0; |
828 | 0 | } |
829 | | |
830 | | static inline double |
831 | | s_Bicubic_interpolate(double *b, double delta) |
832 | 0 | { |
833 | 0 | return b[1] + 0.5 * delta * (b[2] - b[0] |
834 | 0 | + delta * (2.0 * b[0] - 5.0 * b[1] + 4.0 * b[2] - b[3] |
835 | 0 | + delta * (3.0 * (b[1] - b[2]) + b[3] - b[0]))); |
836 | 0 | } |
837 | | |
838 | | static void |
839 | | s_Bicubic_interpolate_pixel(stream_Bicubic_state *const ss, int x_out, |
840 | | int y_out, byte *out) |
841 | 0 | { |
842 | 0 | double v1[4], v2[4], v; |
843 | 0 | double x = x_out * ss->XFactor; |
844 | 0 | double y = y_out * ss->YFactor; |
845 | 0 | double dx = x - floor(x), dy = y - floor(y); |
846 | 0 | int start_x = (int)floor(x) - 1, start_y = (int)floor(y) - 1; |
847 | 0 | int c, i, k; |
848 | |
|
849 | 0 | for (c = 0; c < ss->Colors; c++) { |
850 | 0 | for (i = 0; i < 4; i++) { |
851 | 0 | for (k = 0; k < 4; k++) |
852 | 0 | v1[k] = s_Bicubic_data_at(ss, start_x + k, start_y + i, c); |
853 | 0 | v2[i] = s_Bicubic_interpolate(v1, dx); |
854 | 0 | } |
855 | 0 | v = s_Bicubic_interpolate(v2, dy); |
856 | 0 | out[c] = (v < 0.0f ? 0 : v > 255.0f ? 255 : (byte)floor(v + 0.5)); |
857 | 0 | } |
858 | 0 | } |
859 | | |
860 | | /* Process one buffer. */ |
861 | | static int |
862 | | s_Bicubic_process(stream_state * st, stream_cursor_read * pr, |
863 | | stream_cursor_write * pw, bool last) |
864 | 0 | { |
865 | 0 | stream_Bicubic_state *const ss = (stream_Bicubic_state *) st; |
866 | 0 | int widthOut = s_Downsample_size_out(ss->WidthIn, ss->XFactor, ss->padX); |
867 | 0 | int heightOut = s_Downsample_size_out(ss->HeightIn, ss->YFactor, ss->padY); |
868 | 0 | int req_y; |
869 | |
|
870 | 0 | for (;;) { |
871 | | /* Find required y-offset in data buffer before doing more work */ |
872 | 0 | req_y = (int)floor(ss->y * ss->YFactor) - 1; |
873 | 0 | if (req_y < 0) |
874 | 0 | req_y = 0; |
875 | |
|
876 | 0 | if (ss->y >= heightOut) { |
877 | | /* output has been produced, ignore remaining input */ |
878 | 0 | pr->ptr = pr->limit; |
879 | 0 | return 0; |
880 | 0 | } |
881 | | |
882 | 0 | if ((ss->d_len < ss->d_size) && (pr->ptr < pr->limit)) { |
883 | | /* fill buffer using available data from input stream */ |
884 | 0 | ulong copy = min(ss->d_size - ss->d_len, pr->limit - pr->ptr); |
885 | 0 | memcpy(ss->data + ss->d_len, pr->ptr + 1, copy); |
886 | 0 | ss->d_len += copy; |
887 | 0 | pr->ptr += copy; |
888 | 0 | } |
889 | |
|
890 | 0 | while ((ss->y_in < req_y) && (ss->d_len >= ss->l_size)) { |
891 | | /* remove one line from data buffer to reach req_y */ |
892 | 0 | memmove(ss->data, ss->data + ss->l_size, ss->d_len - ss->l_size); |
893 | 0 | ss->d_len -= ss->l_size; |
894 | 0 | ss->y_in += 1; |
895 | 0 | } |
896 | |
|
897 | 0 | if ((ss->d_len < ss->d_size) || (ss->y_in < req_y)) { |
898 | 0 | if (pr->ptr < pr->limit) |
899 | 0 | continue; |
900 | 0 | if (!last) |
901 | 0 | return 0; /* need more bytes in */ |
902 | 0 | if (ss->y_in < req_y) |
903 | 0 | return 0; /* unable to produce any output */ |
904 | 0 | } |
905 | | |
906 | 0 | while (ss->x < widthOut) { |
907 | 0 | if (pw->ptr + ss->Colors > pw->limit) |
908 | 0 | return 1; /* need more space out */ |
909 | | |
910 | 0 | s_Bicubic_interpolate_pixel(ss, ss->x, ss->y, pw->ptr + 1); |
911 | 0 | ss->x++; |
912 | 0 | pw->ptr += ss->Colors; |
913 | 0 | } |
914 | 0 | ss->x = 0; |
915 | 0 | ss->y += 1; |
916 | 0 | } |
917 | | |
918 | 0 | return 0; |
919 | 0 | } |
920 | | |
921 | | const stream_template s_Bicubic_template = { |
922 | | &st_Bicubic_state, s_Bicubic_init, s_Bicubic_process, 4, 4, |
923 | | s_Bicubic_release, s_Bicubic_set_defaults |
924 | | }; |
925 | | |
926 | | /* ---------------- Image compression chooser ---------------- */ |
927 | | |
928 | | private_st_compr_chooser_state(); |
929 | | |
930 | | /* Initialize the state. */ |
931 | | static int |
932 | | s_compr_chooser_init(stream_state * st) |
933 | 6.25k | { |
934 | 6.25k | stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; |
935 | | |
936 | 6.25k | ss->choice = 0; |
937 | 6.25k | ss->width = ss->height = ss->depth = ss->bits_per_sample = 0; |
938 | 6.25k | ss->sample = 0; |
939 | 6.25k | ss->samples_count = 0; |
940 | 6.25k | ss->bits_left = 0; |
941 | 6.25k | ss->packed_data = 0; |
942 | 6.25k | ss->lower_plateaus = ss->upper_plateaus = 0; |
943 | 6.25k | ss->gradients = 0; |
944 | 6.25k | return 0; |
945 | 6.25k | } |
946 | | |
947 | | /* Set image dimensions. */ |
948 | | int |
949 | | s_compr_chooser_set_dimensions(stream_compr_chooser_state * ss, int width, |
950 | | int height, int depth, int bits_per_sample) |
951 | 6.25k | { |
952 | 6.25k | ss->width = width; |
953 | 6.25k | ss->height = height; |
954 | 6.25k | ss->depth = depth; |
955 | 6.25k | ss->bits_per_sample = bits_per_sample; |
956 | 6.25k | ss->sample = gs_alloc_bytes(ss->memory, (size_t)width * depth, |
957 | 6.25k | "s_compr_chooser_set_dimensions"); |
958 | 6.25k | if (ss->sample == 0) |
959 | 0 | return_error(gs_error_VMerror); |
960 | 6.25k | return 0; |
961 | 6.25k | } |
962 | | |
963 | | /* Release state. */ |
964 | | static void |
965 | | s_compr_chooser_release(stream_state * st) |
966 | 6.25k | { |
967 | 6.25k | stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; |
968 | | |
969 | 6.25k | gs_free_object(ss->memory, ss->sample, "s_compr_chooser_release"); |
970 | 6.25k | } |
971 | | |
972 | | /* Estimate a row for photo/lineart recognition. */ |
973 | | static void |
974 | | s_compr_chooser__estimate_row(stream_compr_chooser_state *const ss, byte *p) |
975 | 1.40M | { |
976 | | /* This function uses a statistical algorithm being not well defined. |
977 | | |
978 | | We compute areas covered by gradients, |
979 | | separately with small width (line art) |
980 | | and with big width (photo). |
981 | | Making the choice based on the areas. |
982 | | |
983 | | Note that we deal with horizontal frequencies only. |
984 | | Dealing with vertical ones would be too expensive. |
985 | | */ |
986 | 1.40M | const int delta = 256 / 16; /* about 1/16 of the color range */ |
987 | 1.40M | const int max_lineart_boundary_width = 3; /* pixels */ |
988 | 1.40M | const int max_gradient_constant = 10; /* pixels */ |
989 | 1.40M | int i, j0 = 0, j1 = 0; |
990 | 1.40M | int w0 = p[0], w1 = p[0], v; |
991 | 1.40M | ulong plateau_count = 0, lower_plateaus = 0; |
992 | 1.40M | ulong upper_plateaus = 0, gradients = 0; |
993 | 1.40M | bool lower = false, upper = false; |
994 | | |
995 | 136M | for (i = 1; i < ss->width; i++) { |
996 | 135M | v = p[i]; |
997 | 135M | if (!lower) { |
998 | 105M | if (w1 < v) { |
999 | 3.29M | if (!upper) |
1000 | 1.98M | j1 = i - 1; |
1001 | 3.29M | w1 = v; |
1002 | 3.29M | upper = true; |
1003 | 102M | } else if (w1 == v && j1 < i - max_gradient_constant) |
1004 | 76.0M | j1 = i - max_gradient_constant; /* inner constant plateaw */ |
1005 | 26.1M | else if (upper && w1 - delta > v) { |
1006 | | /* end of upper plateau at w1-delta...w1 */ |
1007 | 6.57M | for (j0 = i - 1; j0 > j1 && w1 - delta <= p[j0]; j0--) DO_NOTHING; |
1008 | | /* upper plateau j0+1...i-1 */ |
1009 | 1.51M | if(j0 > 0 && i < ss->width - 1) /* ignore sides */ |
1010 | 1.50M | upper_plateaus += i - j0; |
1011 | 1.51M | plateau_count ++; |
1012 | 1.51M | if (j0 > j1) { |
1013 | | /* upgrade j1...j0 */ |
1014 | 81.5k | if (j0 > j1 + max_lineart_boundary_width) |
1015 | 21.6k | gradients += j0 - j1; |
1016 | 81.5k | } |
1017 | 1.51M | j1 = i; |
1018 | 1.51M | upper = false; |
1019 | 1.51M | w0 = w1; |
1020 | 1.51M | continue; |
1021 | 1.51M | } |
1022 | 105M | } |
1023 | 133M | if (!upper) { |
1024 | 108M | if (w0 > v) { |
1025 | 3.61M | if (!lower) |
1026 | 2.25M | j1 = i - 1; |
1027 | 3.61M | w0 = v; |
1028 | 3.61M | lower = true; |
1029 | 104M | } else if (w0 == v && j1 < i - max_gradient_constant) |
1030 | 13.8M | j1 = i - max_gradient_constant; /* inner constant plateaw */ |
1031 | 90.5M | else if (lower && w0 + delta < v) { |
1032 | | /* end of lower plateau at w0...w0+delta */ |
1033 | 16.7M | for (j0 = i - 1; j0 > j1 && w0 + delta >= p[j0]; j0--) DO_NOTHING; |
1034 | | /* lower plateau j0+1...i-1 */ |
1035 | 2.16M | if(j0 > 0 && i < ss->width - 1) /* ignore sides */ |
1036 | 1.87M | lower_plateaus += i - j0; |
1037 | 2.16M | plateau_count ++; |
1038 | 2.16M | if (j0 > j1) { |
1039 | | /* downgrade j1...j0 */ |
1040 | 42.6k | if (j0 > j1 + max_lineart_boundary_width) |
1041 | 14.4k | gradients += j0 - j1; |
1042 | 42.6k | } |
1043 | 2.16M | j1 = i; |
1044 | 2.16M | lower = false; |
1045 | 2.16M | w1 = w0; |
1046 | 2.16M | } |
1047 | 108M | } |
1048 | 133M | } |
1049 | 1.40M | if (plateau_count > ss->width / 6) { |
1050 | | /* Possibly a dithering, can't recognize. |
1051 | | It would be better to estimate frequency histogram rather than |
1052 | | rough quantity, but we hope that the simpler test can work fine. |
1053 | | */ |
1054 | 1.39M | } else if (!plateau_count) /* a pseudo-constant color through entire row */ |
1055 | 1.39M | DO_NOTHING; /* ignore such lines */ |
1056 | 770k | else { |
1057 | 770k | int plateaus; |
1058 | 770k | ss->lower_plateaus += lower_plateaus; |
1059 | 770k | ss->upper_plateaus += upper_plateaus; |
1060 | 770k | ss->gradients += gradients; |
1061 | 770k | plateaus = min(ss->lower_plateaus, ss->upper_plateaus); /* (fore/back)ground */ |
1062 | 770k | if (ss->gradients >= 10000 && ss->gradients > plateaus / 6) |
1063 | 47 | ss->choice = 1; /* choice is made : photo */ |
1064 | 770k | else if (plateaus >= 100000 && plateaus / 5000 >= ss->gradients) |
1065 | 191 | ss->choice = 2; /* choice is made : lineart */ |
1066 | 770k | } |
1067 | 1.40M | } |
1068 | | |
1069 | | /* Recognize photo/lineart. */ |
1070 | | static void |
1071 | | s_compr_chooser__recognize(stream_compr_chooser_state * ss) |
1072 | 839k | { |
1073 | 839k | int i; |
1074 | 839k | byte *p = ss->sample; |
1075 | | |
1076 | 2.24M | for (i = 0; i < ss->depth; i++, p += ss->width) |
1077 | 1.40M | s_compr_chooser__estimate_row(ss, p); |
1078 | | /* todo: make decision */ |
1079 | 839k | } |
1080 | | |
1081 | | /* Uppack data and recognize photo/lineart. */ |
1082 | | static void |
1083 | | s_compr_chooser__unpack_and_recognize(stream_compr_chooser_state *const ss, |
1084 | | const byte *data, int length) |
1085 | 454k | { |
1086 | | /* |
1087 | | * Input samples are packed ABCABCABC..., but the sample[] array of |
1088 | | * unpacked values is stored AAA...BBB...CCC. i counts samples within |
1089 | | * a pixel, multiplied by width; j counts pixels. |
1090 | | */ |
1091 | 454k | uint i = (ss->samples_count % ss->depth) * ss->width; |
1092 | 454k | uint j = ss->samples_count / ss->depth; |
1093 | 454k | const byte *p = data; |
1094 | 454k | int l = length; |
1095 | | |
1096 | 29.4M | while (l) { |
1097 | 28.9M | if (ss->bits_left <= 8) { |
1098 | 28.9M | uint k = (sizeof(ss->packed_data) * 8 - ss->bits_left) / 8; |
1099 | | |
1100 | 28.9M | k = min(k, l); |
1101 | 144M | for (; k; k--, l--, p++, ss->bits_left += 8) |
1102 | 115M | ss->packed_data = (ss->packed_data << 8) + *p; |
1103 | 28.9M | } |
1104 | 165M | while (ss->bits_left >= ss->bits_per_sample) { |
1105 | 136M | uint k = ss->bits_left - ss->bits_per_sample; |
1106 | 136M | ulong v = ss->packed_data >> k; |
1107 | | |
1108 | 136M | ss->packed_data -= (v << k); |
1109 | 136M | ss->bits_left -= ss->bits_per_sample; |
1110 | 136M | if (ss->bits_per_sample > 8) |
1111 | 0 | v >>= ss->bits_per_sample - 8; |
1112 | 136M | else |
1113 | 136M | v <<= 8 - ss->bits_per_sample; |
1114 | 136M | ss->sample[i + j] = (byte)v; /* scaled to 0...255 */ |
1115 | 136M | i += ss->width; |
1116 | 136M | if (i >= ss->width * ss->depth) |
1117 | 80.5M | i = 0, j++; |
1118 | 136M | ss->samples_count++; |
1119 | 136M | if (ss->samples_count >= ss->width * ss->depth) { |
1120 | 839k | s_compr_chooser__recognize(ss); |
1121 | 839k | ss->packed_data = 0; |
1122 | 839k | ss->bits_left = 0; |
1123 | 839k | ss->samples_count = 0; |
1124 | 839k | i = j = 0; |
1125 | 839k | } |
1126 | 136M | } |
1127 | 28.9M | } |
1128 | 454k | } |
1129 | | |
1130 | | /* Process a buffer. */ |
1131 | | static int |
1132 | | s_compr_chooser_process(stream_state * st, stream_cursor_read * pr, |
1133 | | stream_cursor_write * pw, bool last) |
1134 | 454k | { |
1135 | 454k | stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; |
1136 | 454k | int l = pr->limit - pr->ptr; |
1137 | | |
1138 | 454k | if (ss->width >= 3) /* Can't process narrow images. */ |
1139 | 454k | s_compr_chooser__unpack_and_recognize(ss, pr->ptr + 1, l); |
1140 | 454k | pr->ptr += l; |
1141 | 454k | return 0; |
1142 | 454k | } |
1143 | | |
1144 | | const stream_template s_compr_chooser_template = { |
1145 | | &st_compr_chooser_state, s_compr_chooser_init, s_compr_chooser_process, 1, 1, |
1146 | | s_compr_chooser_release, 0 /* NULL */ |
1147 | | }; |
1148 | | |
1149 | | /* Get choice */ |
1150 | | uint |
1151 | | s_compr_chooser__get_choice(stream_compr_chooser_state *ss, bool force) |
1152 | 508k | { |
1153 | 508k | ulong plateaus = min(ss->lower_plateaus, ss->upper_plateaus); |
1154 | | |
1155 | 508k | if (ss->choice) |
1156 | 198 | return ss->choice; |
1157 | 507k | if (force) { |
1158 | 665 | if (ss->gradients > plateaus / 12) /* messenger16.pdf, page 3. */ |
1159 | 330 | return 1; /* photo */ |
1160 | 335 | else if (plateaus && plateaus / 5000 >= ss->gradients) |
1161 | 281 | return 2; /* lineart */ |
1162 | 665 | } |
1163 | 507k | return 0; |
1164 | 507k | } |
1165 | | |
1166 | | /* ---------------- An image color conversion filter ---------------- */ |
1167 | | |
1168 | | private_st_image_colors_state(); |
1169 | | |
1170 | | /* Initialize the state. */ |
1171 | | static int |
1172 | | s_image_colors_init(stream_state * st) |
1173 | 1.62k | { |
1174 | 1.62k | stream_image_colors_state *const ss = (stream_image_colors_state *) st; |
1175 | | |
1176 | 1.62k | ss->width = ss->height = ss->depth = ss->bits_per_sample = 0; |
1177 | 1.62k | ss->output_bits_buffer = 0; |
1178 | 1.62k | ss->output_bits_buffered = 0; |
1179 | 1.62k | ss->output_depth = 1; |
1180 | 1.62k | ss->output_component_index = ss->output_depth; |
1181 | 1.62k | ss->output_bits_per_sample = 1; |
1182 | 1.62k | ss->output_component_bits_written = 0; |
1183 | 1.62k | ss->raster = 0; |
1184 | 1.62k | ss->row_bits = 0; |
1185 | 1.62k | ss->row_bits_passed = 0; |
1186 | 1.62k | ss->row_alignment_bytes = 0; |
1187 | 1.62k | ss->row_alignment_bytes_left = 0; |
1188 | 1.62k | ss->input_component_index = 0; |
1189 | 1.62k | ss->input_bits_buffer = 0; |
1190 | 1.62k | ss->input_bits_buffered = 0; |
1191 | 1.62k | ss->convert_color = 0; |
1192 | 1.62k | ss->pcs = 0; |
1193 | 1.62k | ss->pdev = 0; |
1194 | 1.62k | ss->pgs = 0; |
1195 | 1.62k | return 0; |
1196 | 1.62k | } |
1197 | | |
1198 | | static int |
1199 | | s_image_colors_convert_color_to_mask(stream_image_colors_state *ss) |
1200 | 0 | { |
1201 | 0 | int i, ii; |
1202 | |
|
1203 | 0 | for (i = ii = 0; i < ss->depth; i++, ii += 2) |
1204 | 0 | if (ss->input_color[i] < ss->MaskColor[ii] || |
1205 | 0 | ss->input_color[i] > ss->MaskColor[ii + 1]) |
1206 | 0 | break; |
1207 | 0 | ss->output_color[0] = (i < ss->depth ? 1 : 0); |
1208 | 0 | return 0; |
1209 | 0 | } |
1210 | | |
1211 | | static int |
1212 | | s_image_colors_convert_to_device_color(stream_image_colors_state * ss) |
1213 | 84.2M | { |
1214 | 84.2M | gs_client_color cc; |
1215 | 84.2M | gx_device_color dc; |
1216 | 84.2M | int i, code; |
1217 | 84.2M | double v0 = (1 << ss->bits_per_sample) - 1; |
1218 | 84.2M | double v1 = (1 << ss->output_bits_per_sample) - 1; |
1219 | 84.2M | gx_device *target; |
1220 | | |
1221 | 84.2M | target = ss->pdev; |
1222 | 84.2M | while(target->child) |
1223 | 0 | target = target->child; |
1224 | | |
1225 | 197M | for (i = 0; i < ss->depth; i++) |
1226 | 113M | cc.paint.values[i] = ss->input_color[i] * |
1227 | 113M | (ss->Decode[i * 2 + 1] - ss->Decode[i * 2]) / v0 + ss->Decode[i * 2]; |
1228 | | |
1229 | 84.2M | code = ss->pcs->type->remap_color(&cc, ss->pcs, &dc, ss->pgs, |
1230 | 84.2M | target, gs_color_select_texture); |
1231 | 84.2M | if (code < 0) |
1232 | 0 | return code; |
1233 | 337M | for (i = 0; i < ss->output_depth; i++) { |
1234 | 252M | uint m = (1 << target->color_info.comp_bits[i]) - 1; |
1235 | 252M | uint w = (dc.colors.pure >> target->color_info.comp_shift[i]) & m; |
1236 | | |
1237 | 252M | ss->output_color[i] = (uint)(v1 * w / m + 0.5); |
1238 | 252M | } |
1239 | 84.2M | return 0; |
1240 | 84.2M | } |
1241 | | |
1242 | | /* Set mask colors dimensions. */ |
1243 | | void |
1244 | | s_image_colors_set_mask_colors(stream_image_colors_state * ss, uint *MaskColor) |
1245 | 0 | { |
1246 | 0 | ss->convert_color = s_image_colors_convert_color_to_mask; |
1247 | 0 | memcpy(ss->MaskColor, MaskColor, ss->depth * sizeof(MaskColor[0]) * 2); |
1248 | 0 | } |
1249 | | |
1250 | | /* Set image dimensions. */ |
1251 | | void |
1252 | | s_image_colors_set_dimensions(stream_image_colors_state * ss, |
1253 | | int width, int height, int input_width, |
1254 | | int depth, int bits_per_sample) |
1255 | 1.62k | { |
1256 | 1.62k | ss->width = width; |
1257 | 1.62k | ss->height = height; |
1258 | 1.62k | ss->depth = depth; |
1259 | 1.62k | ss->bits_per_sample = bits_per_sample; |
1260 | 1.62k | ss->row_bits = bits_per_sample * depth * input_width; |
1261 | 1.62k | ss->raster = bitmap_raster(ss->row_bits); |
1262 | 1.62k | ss->row_alignment_bytes = 0; /* (ss->raster * 8 - ss->row_bits) / 8) doesn't work. */ |
1263 | 1.62k | } |
1264 | | |
1265 | | void |
1266 | | s_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev, |
1267 | | const gs_color_space *pcs, const gs_gstate *pgs, |
1268 | | float *Decode) |
1269 | 1.62k | { |
1270 | 1.62k | ss->output_depth = pdev->color_info.num_components; |
1271 | 1.62k | ss->output_component_index = ss->output_depth; |
1272 | 1.62k | ss->output_bits_per_sample = pdev->color_info.comp_bits[0]; /* Same precision for all components. */ |
1273 | 1.62k | ss->convert_color = s_image_colors_convert_to_device_color; |
1274 | 1.62k | ss->pdev = pdev; |
1275 | 1.62k | while(ss->pdev->parent) |
1276 | 0 | ss->pdev = ss->pdev->parent; |
1277 | | |
1278 | 1.62k | ss->pcs = pcs; |
1279 | 1.62k | ss->pgs = pgs; |
1280 | 1.62k | memcpy(ss->Decode, Decode, ss->depth * sizeof(Decode[0]) * 2); |
1281 | 1.62k | } |
1282 | | |
1283 | | /* Process a buffer. */ |
1284 | | static int |
1285 | | s_image_colors_process(stream_state * st, stream_cursor_read * pr, |
1286 | | stream_cursor_write * pw, bool last) |
1287 | 2.37M | { |
1288 | 2.37M | stream_image_colors_state *const ss = (stream_image_colors_state *) st; |
1289 | | |
1290 | 200M | for (;;) { |
1291 | 200M | if (pw->ptr >= pw->limit) |
1292 | 1.31M | return 1; |
1293 | 198M | if (ss->row_bits_passed >= ss->row_bits) { |
1294 | 108k | ss->row_alignment_bytes_left = ss->row_alignment_bytes; |
1295 | 108k | ss->input_bits_buffered = 0; |
1296 | 108k | ss->input_bits_buffer = 0; /* Just to simplify the debugging. */ |
1297 | 108k | if (ss->output_bits_buffered) { |
1298 | 0 | *(++pw->ptr) = ss->output_bits_buffer; |
1299 | 0 | ss->output_bits_buffered = 0; |
1300 | 0 | ss->output_bits_buffer = 0; |
1301 | 0 | } |
1302 | 108k | ss->row_bits_passed = 0; |
1303 | 108k | continue; |
1304 | 108k | } |
1305 | 198M | if (ss->row_alignment_bytes_left) { |
1306 | 0 | uint k = pr->limit - pr->ptr; |
1307 | |
|
1308 | 0 | if (k > ss->row_alignment_bytes_left) |
1309 | 0 | k = ss->row_alignment_bytes_left; |
1310 | 0 | pr->ptr += k; |
1311 | 0 | ss->row_alignment_bytes_left -= k; |
1312 | 0 | if (pr->ptr >= pr->limit) |
1313 | 0 | return 0; |
1314 | 0 | } |
1315 | 198M | if (ss->output_component_index < ss->output_depth) { |
1316 | 337M | for (;ss->output_component_index < ss->output_depth;) { |
1317 | 253M | uint fitting = (uint)(8 - ss->output_bits_buffered); |
1318 | 253M | uint v, w, u, n, m; |
1319 | | |
1320 | 253M | if (pw->ptr >= pw->limit) |
1321 | 658k | return 1; |
1322 | 252M | v = ss->output_color[ss->output_component_index]; |
1323 | 252M | n = ss->output_bits_per_sample - ss->output_component_bits_written; /* no. of bits left */ |
1324 | 252M | w = v - ((v >> n) << n); /* the current component without written bits. */ |
1325 | 252M | if (fitting > n) |
1326 | 0 | fitting = n; /* no. of bits to write. */ |
1327 | 252M | m = n - fitting; /* no. of bits will left. */ |
1328 | 252M | u = w >> m; /* bits to write (near lsb). */ |
1329 | 252M | ss->output_bits_buffer |= u << (8 - ss->output_bits_buffered - fitting); |
1330 | 252M | ss->output_bits_buffered += fitting; |
1331 | 252M | if (ss->output_bits_buffered >= 8) { |
1332 | 252M | *(++pw->ptr) = ss->output_bits_buffer; |
1333 | 252M | ss->output_bits_buffered = 0; |
1334 | 252M | ss->output_bits_buffer = 0; |
1335 | 252M | } |
1336 | 252M | ss->output_component_bits_written += fitting; |
1337 | 252M | if (ss->output_component_bits_written >= ss->output_bits_per_sample) { |
1338 | 252M | ss->output_component_index++; |
1339 | 252M | ss->output_component_bits_written = 0; |
1340 | 252M | } |
1341 | 252M | } |
1342 | 84.2M | ss->row_bits_passed += ss->bits_per_sample * ss->depth; |
1343 | 84.2M | continue; |
1344 | 84.9M | } |
1345 | 113M | if (ss->input_bits_buffered < ss->bits_per_sample) { |
1346 | 102M | if (pr->ptr >= pr->limit) |
1347 | 398k | return 0; |
1348 | 101M | ss->input_bits_buffer = (ss->input_bits_buffer << 8) | *++pr->ptr; |
1349 | 101M | ss->input_bits_buffered += 8; |
1350 | | /* fixme: delay shifting the input ptr until input_bits_buffer is cleaned. */ |
1351 | 101M | } |
1352 | 113M | if (ss->input_bits_buffered >= ss->bits_per_sample) { |
1353 | 113M | uint w; |
1354 | | |
1355 | 113M | ss->input_bits_buffered -= ss->bits_per_sample; |
1356 | 113M | ss->input_color[ss->input_component_index] = w = ss->input_bits_buffer >> ss->input_bits_buffered; |
1357 | 113M | ss->input_bits_buffer &= ~(w << ss->input_bits_buffered); |
1358 | 113M | ss->input_component_index++; |
1359 | 113M | if (ss->input_component_index >= ss->depth) { |
1360 | 84.2M | int code = ss->convert_color(ss); |
1361 | | |
1362 | 84.2M | if (code < 0) |
1363 | 0 | return ERRC; |
1364 | 84.2M | ss->output_component_index = 0; |
1365 | 84.2M | ss->input_component_index = 0; |
1366 | 84.2M | } |
1367 | 113M | } |
1368 | 113M | } |
1369 | 2.37M | } |
1370 | | |
1371 | | const stream_template s__image_colors_template = { |
1372 | | &st_stream_image_colors_state, s_image_colors_init, s_image_colors_process, 1, 1, |
1373 | | NULL, NULL |
1374 | | }; |