/src/ghostpdl/base/scfe.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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 | | /* CCITTFax encoding filter */ |
18 | | #include "stdio_.h" /* includes std.h */ |
19 | | #include "memory_.h" |
20 | | #include "gdebug.h" |
21 | | #include "strimpl.h" |
22 | | #include "scf.h" |
23 | | #include "scfx.h" |
24 | | |
25 | | /* ------ Macros and support routines ------ */ |
26 | | |
27 | | /* Statistics */ |
28 | | /* #define COLLECT_STATS_SCFE */ |
29 | | |
30 | | #ifdef COLLECT_STATS_SCFE |
31 | | typedef struct stats_runs_s { |
32 | | ulong termination[64]; |
33 | | ulong make_up[41]; |
34 | | } stats_runs_t; |
35 | | static stats_runs_t stats_white_runs, stats_black_runs; |
36 | | |
37 | | #define COUNT_RUN(tab, i) (tab)[i]++; |
38 | | |
39 | | static void |
40 | | print_run_stats(const gs_memory_t *mem, const stats_runs_t * stats) |
41 | | { |
42 | | int i; |
43 | | ulong total; |
44 | | |
45 | | for (i = 0, total = 0; i < 41; i++) |
46 | | dmprintf1(mem, " %lu", stats->make_up[i]), |
47 | | total += stats->make_up[i]; |
48 | | dmprintf1(mem, " total=%lu\n\t", total); |
49 | | for (i = 0, total = 0; i < 64; i++) |
50 | | dmprintf1(mem, " %lu", stats->termination[i]), |
51 | | total += stats->termination[i]; |
52 | | dmprintf1(mem, " total=%lu\n", total); |
53 | | } |
54 | | |
55 | | #else /* !defined(COLLECT_STATS_SCFE) */ |
56 | | |
57 | 500M | #define COUNT_RUN(cnt, i) DO_NOTHING |
58 | | |
59 | | #endif /* DEBUG */ |
60 | | |
61 | | /* Put a run onto the output stream. */ |
62 | | /* Free variables: q, bits, bits_left. */ |
63 | | |
64 | 471M | #define CF_PUT_RUN(ss, lenv, rt, stats)\ |
65 | 471M | BEGIN\ |
66 | 471M | cfe_run rr;\ |
67 | 471M | \ |
68 | 471M | if ( lenv >= 64 ) {\ |
69 | 24.2M | hce_store_state();\ |
70 | 24.2M | q = cf_put_long_run(ss, q, lenv, &rt);\ |
71 | 24.2M | hce_load_state();\ |
72 | 24.2M | lenv &= 63;\ |
73 | 24.2M | }\ |
74 | 471M | rr = rt.termination[lenv];\ |
75 | 471M | COUNT_RUN(stats.termination, lenv);\ |
76 | 471M | hc_put_value(ss, q, rr.code, rr.code_length);\ |
77 | 471M | END |
78 | | |
79 | | static byte * |
80 | | cf_put_long_run(stream_CFE_state * ss, byte * q, int lenv, const cf_runs * prt) |
81 | 24.2M | { |
82 | 24.2M | hce_declare_state; |
83 | 24.2M | cfe_run rr; |
84 | | |
85 | | #ifdef COLLECT_STATS_SCFE |
86 | | stats_runs_t *pstats = |
87 | | (prt == &cf_white_runs ? &stats_white_runs : &stats_black_runs); |
88 | | #endif |
89 | | |
90 | 24.2M | hce_load_state(); |
91 | 28.3M | while (lenv >= 2560 + 64) { |
92 | 4.11M | rr = prt->make_up[40]; |
93 | 4.11M | COUNT_RUN(pstats->make_up, 40); |
94 | 4.11M | hc_put_value(ss, q, rr.code, rr.code_length); |
95 | 4.11M | lenv -= 2560; |
96 | 4.11M | } |
97 | 24.2M | rr = prt->make_up[lenv >> 6]; |
98 | 24.2M | COUNT_RUN(pstats->make_up, lenv >> 6); |
99 | 24.2M | hc_put_value(ss, q, rr.code, rr.code_length); |
100 | 24.2M | hce_store_state(); |
101 | 24.2M | return q; |
102 | 24.2M | } |
103 | | |
104 | | #define CF_PUT_WHITE_RUN(ss, lenv)\ |
105 | 243M | CF_PUT_RUN(ss, lenv, cf_white_runs, stats_white_runs) |
106 | | |
107 | | #define CF_PUT_BLACK_RUN(ss, lenv)\ |
108 | 228M | CF_PUT_RUN(ss, lenv, cf_black_runs, stats_black_runs) |
109 | | |
110 | | /* ------ CCITTFaxEncode ------ */ |
111 | | |
112 | | private_st_CFE_state(); |
113 | | |
114 | | static void s_CFE_release(stream_state *); |
115 | | |
116 | | /* Set default parameter values. */ |
117 | | static void |
118 | | s_CFE_set_defaults(register stream_state * st) |
119 | 141k | { |
120 | 141k | stream_CFE_state *const ss = (stream_CFE_state *) st; |
121 | | |
122 | 141k | s_CFE_set_defaults_inline(ss); |
123 | 141k | } |
124 | | |
125 | | /* Initialize CCITTFaxEncode filter */ |
126 | | static int |
127 | | s_CFE_init(register stream_state * st) |
128 | 2.43M | { |
129 | 2.43M | stream_CFE_state *const ss = (stream_CFE_state *) st; |
130 | 2.43M | int columns = ss->Columns; |
131 | | |
132 | | /* |
133 | | * The worst case for encoding is alternating white and black pixels. |
134 | | * For 1-D encoding, the worst case is 9 bits per 2 pixels; for 2-D |
135 | | * (horizontal), 12 bits per 2 pixels. However, for 2D vertical encoding |
136 | | * an offset 3 vertically encoded requires 7 bits of encoding. So we need |
137 | | * to allow 14 bits for this, not 12 (see bug 696413). To fill out a scan line, |
138 | | * we may add up to 6 12-bit EOL codes. |
139 | | */ |
140 | 2.43M | int code_bytes = |
141 | 2.43M | (((columns * (ss->K == 0 ? 9 : 14)) + 15) >> 4) + 20; /* add slop */ |
142 | 2.43M | int raster = ss->raster = |
143 | 2.43M | ROUND_UP((columns + 7) >> 3, ss->DecodedByteAlign); |
144 | | |
145 | 2.43M | s_hce_init_inline(ss); |
146 | 2.43M | ss->lbuf = ss->lprev = ss->lcode = 0; /* in case we have to release */ |
147 | 2.43M | if (columns > cfe_max_width) |
148 | 0 | return ERRC; |
149 | | /****** WRONG ******/ |
150 | | /* Because skip_white_pixels can look as many as 4 bytes ahead, */ |
151 | | /* we need to allow 4 extra bytes at the end of the row buffers. */ |
152 | 2.43M | ss->lbufstart = gs_alloc_bytes(st->memory, raster + 8, "CFE lbuf"); |
153 | 2.43M | ss->lcode = gs_alloc_bytes(st->memory, code_bytes, "CFE lcode"); |
154 | 2.43M | if (ss->lbufstart == 0 || ss->lcode == 0) { |
155 | 0 | s_CFE_release(st); |
156 | 0 | return ERRC; |
157 | | /****** WRONG ******/ |
158 | 0 | } |
159 | 2.43M | ss->lbuf = ss->lbufstart + 4; |
160 | 2.43M | memset(ss->lbuf + raster, 0, 4); /* to pacify Valgrind */ |
161 | 2.43M | if (ss->K != 0) { |
162 | 2.42M | ss->lprevstart = gs_alloc_bytes(st->memory, raster + 8, "CFE lprev"); |
163 | 2.42M | if (ss->lprevstart == 0) { |
164 | 0 | s_CFE_release(st); |
165 | 0 | return ERRC; |
166 | | /****** WRONG ******/ |
167 | 0 | } |
168 | 2.42M | ss->lprev = ss->lprevstart + 4; |
169 | | /* Clear the initial reference line for 2-D encoding. */ |
170 | | /* Make sure it is terminated properly. */ |
171 | 2.42M | memset(ss->lprev, (ss->BlackIs1 ? 0 : 0xff), raster + 4); /* +4 to pacify Valgrind */ |
172 | 2.42M | if (columns & 7) |
173 | 114k | ss->lprev[raster - 1] ^= 0x80 >> (columns & 7); |
174 | 2.31M | else |
175 | 2.31M | ss->lprev[raster] = ~ss->lprev[0]; |
176 | 2.42M | } |
177 | 2.43M | ss->read_count = raster; |
178 | 2.43M | ss->write_count = 0; |
179 | 2.43M | ss->k_left = (ss->K > 0 ? 1 : ss->K); |
180 | 2.43M | ss->max_code_bytes = code_bytes; |
181 | 2.43M | return 0; |
182 | 2.43M | } |
183 | | |
184 | | /* Release the filter. */ |
185 | | static void |
186 | | s_CFE_release(stream_state * st) |
187 | 2.43M | { |
188 | 2.43M | stream_CFE_state *const ss = (stream_CFE_state *) st; |
189 | | |
190 | 2.43M | gs_free_object(st->memory, ss->lprevstart, "CFE lprev(close)"); |
191 | 2.43M | gs_free_object(st->memory, ss->lcode, "CFE lcode(close)"); |
192 | 2.43M | gs_free_object(st->memory, ss->lbufstart, "CFE lbuf(close)"); |
193 | 2.43M | } |
194 | | |
195 | | /* Flush the buffer */ |
196 | | static void cf_encode_1d(stream_CFE_state *, const byte *, |
197 | | stream_cursor_write *); |
198 | | static void cf_encode_2d(stream_CFE_state *, const byte *, |
199 | | stream_cursor_write *, const byte *); |
200 | | static int |
201 | | s_CFE_process(stream_state * st, stream_cursor_read * pr, |
202 | | stream_cursor_write * pw, bool last) |
203 | 195M | { |
204 | 195M | stream_CFE_state *const ss = (stream_CFE_state *) st; |
205 | 195M | const byte *rlimit = pr->limit; |
206 | 195M | byte *wlimit = pw->limit; |
207 | 195M | int raster = ss->raster; |
208 | 195M | byte end_mask = 1 << (-ss->Columns & 7); |
209 | 195M | int status = 0; |
210 | | |
211 | | /* Update the pointers we actually use, in case GC moved the buffer */ |
212 | 195M | ss->lbuf = ss->lbufstart + 4; |
213 | 195M | ss->lprev = ss->lprevstart + 4; |
214 | | |
215 | 276M | for (;;) { |
216 | 276M | stream_cursor_write w; |
217 | | |
218 | 276M | if_debug2m('w', ss->memory, "[w]CFE: read_count = %d, write_count=%d,\n", |
219 | 276M | ss->read_count, ss->write_count); |
220 | 276M | if_debug6m('w', ss->memory, " pr = "PRI_INTPTR"(%d)"PRI_INTPTR", pw = "PRI_INTPTR"(%d)"PRI_INTPTR"\n", |
221 | 276M | (intptr_t) pr->ptr, (int)(rlimit - pr->ptr), (intptr_t) rlimit, |
222 | 276M | (intptr_t) pw->ptr, (int)(wlimit - pw->ptr), (intptr_t) wlimit); |
223 | 276M | if (ss->write_count) { |
224 | | /* Copy more of an encoded line to the caller. */ |
225 | 25.4M | int wcount = wlimit - pw->ptr; |
226 | 25.4M | int ccount = min(wcount, ss->write_count); |
227 | | |
228 | 25.4M | memcpy(pw->ptr + 1, ss->lcode + ss->code_bytes - ss->write_count, |
229 | 25.4M | ccount); |
230 | 25.4M | pw->ptr += ccount; |
231 | 25.4M | if ((ss->write_count -= ccount) > 0) { |
232 | 337k | status = 1; |
233 | 337k | break; |
234 | 337k | } |
235 | 25.4M | } |
236 | 276M | if (ss->read_count) { |
237 | | /* Copy more of an unencoded line from the caller. */ |
238 | 276M | int rcount = rlimit - pr->ptr; |
239 | 276M | int ccount = min(rcount, ss->read_count); |
240 | | |
241 | 276M | if (rcount == 0 && last) |
242 | 2.36M | break; |
243 | 274M | memcpy(ss->lbuf + raster - ss->read_count, |
244 | 274M | pr->ptr + 1, ccount); |
245 | 274M | pr->ptr += ccount; |
246 | 274M | if ((ss->read_count -= ccount) != 0) |
247 | 193M | break; |
248 | 274M | } |
249 | | /* |
250 | | * We have a full scan line in lbuf. Ensure that it ends with |
251 | | * two polarity changes. |
252 | | */ |
253 | 80.8M | { |
254 | 80.8M | byte *end = ss->lbuf + raster - 1; |
255 | 80.8M | byte end_bit = *end & end_mask; |
256 | 80.8M | byte not_bit = end_bit ^ end_mask; |
257 | | |
258 | 80.8M | *end &= -end_mask; |
259 | 80.8M | if (end_mask == 1) |
260 | 70.8M | end[1] = (end_bit ? 0x40 : 0x80); |
261 | 9.95M | else if (end_mask == 2) |
262 | 924k | *end |= not_bit >> 1, end[1] = end_bit << 7; |
263 | 9.02M | else |
264 | 9.02M | *end |= (not_bit >> 1) | (end_bit >> 2); |
265 | 80.8M | } |
266 | | /* |
267 | | * Write the output directly to the caller's buffer if it's large |
268 | | * enough, otherwise to our own buffer. |
269 | | */ |
270 | 80.8M | if (wlimit - pw->ptr >= ss->max_code_bytes) { |
271 | 18.8M | w = *pw; |
272 | 61.9M | } else { |
273 | 61.9M | w.ptr = ss->lcode - 1; |
274 | 61.9M | w.limit = w.ptr + ss->max_code_bytes; |
275 | 61.9M | } |
276 | | #ifdef DEBUG |
277 | | if (ss->K > 0) { |
278 | | if_debug2m('w', ss->memory, "[w2]new %d-D row, k_left=%d\n", |
279 | | (ss->k_left == 1 ? 1 : 2), ss->k_left); |
280 | | } else { |
281 | | if_debug1m('w', ss->memory, "[w%d]new row\n", (ss->K < 0 ? 2 : 1)); |
282 | | } |
283 | | #endif |
284 | | /* |
285 | | * Write an EOL (actually a "beginning of line") if requested. |
286 | | */ |
287 | 80.8M | if (ss->EndOfLine) { |
288 | 15.1M | const cfe_run *rp = |
289 | 15.1M | (ss->K <= 0 ? &cf_run_eol : |
290 | 15.1M | ss->k_left > 1 ? &cf2_run_eol_2d : |
291 | 0 | &cf2_run_eol_1d); |
292 | 15.1M | cfe_run run; |
293 | | |
294 | 15.1M | hce_declare_state; |
295 | | |
296 | 15.1M | hce_load_state(); |
297 | 15.1M | if (ss->EncodedByteAlign) { |
298 | 0 | run = *rp; |
299 | | /* Pad the run on the left */ |
300 | | /* so it winds up byte-aligned. */ |
301 | 0 | run.code_length += |
302 | 0 | (bits_left - run_eol_code_length) & 7; |
303 | 0 | if (run.code_length > 16) /* <= 23 */ |
304 | 0 | bits_left -= run.code_length & 7, |
305 | 0 | run.code_length = 16; |
306 | 0 | rp = &run; |
307 | 0 | } |
308 | 15.1M | hc_put_code(ss, w.ptr, rp); |
309 | 15.1M | hce_store_state(); |
310 | 65.6M | } else if (ss->EncodedByteAlign) |
311 | 0 | ss->bits_left &= ~7; |
312 | | /* Encode the line. */ |
313 | 80.8M | if (ss->K == 0) |
314 | 15.1M | cf_encode_1d(ss, ss->lbuf, &w); /* pure 1-D */ |
315 | 65.6M | else if (ss->K < 0) |
316 | 65.6M | cf_encode_2d(ss, ss->lbuf, &w, ss->lprev); /* pure 2-D */ |
317 | 0 | else if (--(ss->k_left)) /* mixed, use 2-D */ |
318 | 0 | cf_encode_2d(ss, ss->lbuf, &w, ss->lprev); |
319 | 0 | else { /* mixed, use 1-D */ |
320 | 0 | cf_encode_1d(ss, ss->lbuf, &w); |
321 | 0 | ss->k_left = ss->K; |
322 | 0 | } |
323 | | /* |
324 | | * If we didn't write directly to the client's buffer, schedule |
325 | | * the output data to be written. |
326 | | */ |
327 | 80.8M | if (w.limit == wlimit) |
328 | 18.8M | pw->ptr = w.ptr; |
329 | 61.9M | else |
330 | 61.9M | ss->write_count = ss->code_bytes = w.ptr - (ss->lcode - 1); |
331 | 80.8M | if (ss->K != 0) { |
332 | | /* In 2-D modes, swap the current and previous scan lines. */ |
333 | 65.6M | byte *temp = ss->lbuf; |
334 | 65.6M | byte *temp1 = ss->lbufstart; |
335 | | |
336 | 65.6M | ss->lbuf = ss->lprev; |
337 | 65.6M | ss->lbufstart = ss->lprevstart; |
338 | 65.6M | ss->lprev = temp; |
339 | 65.6M | ss->lprevstart = temp1; |
340 | 65.6M | } |
341 | | /* Note that the input buffer needs refilling. */ |
342 | 80.8M | ss->read_count = raster; |
343 | 80.8M | } |
344 | | /* |
345 | | * When we exit from the loop, we know that write_count = 0, and |
346 | | * there is no line waiting to be processed in the input buffer. |
347 | | */ |
348 | 195M | if (last && status == 0) { |
349 | 2.36M | const cfe_run *rp = |
350 | 2.36M | (ss->K > 0 ? &cf2_run_eol_1d : &cf_run_eol); |
351 | 2.36M | int i = (!ss->EndOfBlock ? 0 : ss->K < 0 ? 2 : 6); |
352 | 2.36M | uint bits_to_write = |
353 | 2.36M | hc_bits_size - ss->bits_left + i * rp->code_length; |
354 | 2.36M | byte *q = pw->ptr; |
355 | | |
356 | 2.36M | hce_declare_state; |
357 | | |
358 | 2.36M | if (wlimit - q < (bits_to_write + 7) >> 3) { |
359 | 217k | status = 1; |
360 | 217k | goto out; |
361 | 217k | } |
362 | 2.14M | hce_load_state(); |
363 | 2.14M | if (ss->EncodedByteAlign) |
364 | 0 | bits_left &= ~7; |
365 | 6.15M | while (--i >= 0) |
366 | 4.00M | hc_put_code(ss, q, rp); |
367 | | /* Force out the last byte or bytes. */ |
368 | 2.14M | pw->ptr = hc_put_last_bits((stream_hc_state *) ss, q); |
369 | 2.14M | } |
370 | 195M | out: |
371 | 195M | if_debug9m('w', ss->memory, "[w]CFE exit %d: read_count = %d, write_count = %d,\n" |
372 | 195M | " pr = "PRI_INTPTR"(%d)"PRI_INTPTR"; pw = "PRI_INTPTR"(%d)"PRI_INTPTR"\n", |
373 | 195M | status, ss->read_count, ss->write_count, |
374 | 195M | (intptr_t) pr->ptr, (int)(rlimit - pr->ptr), (intptr_t) rlimit, |
375 | 195M | (intptr_t) pw->ptr, (int)(wlimit - pw->ptr), (intptr_t) wlimit); |
376 | | #ifdef DEBUG |
377 | | if (pr->ptr > rlimit || pw->ptr > wlimit) { |
378 | | lprintf("Pointer overrun!\n"); |
379 | | status = ERRC; |
380 | | } |
381 | | #ifdef COLLECT_STATS_SCFE |
382 | | if (gs_debug_c('w') && status == 1) { |
383 | | dmlputs(ss->memory, "[w]white runs:"); |
384 | | print_run_stats(ss->memory, &stats_white_runs); |
385 | | dmlputs(ss->memory, "[w]black runs:"); |
386 | | print_run_stats(ss->memory, &stats_black_runs); |
387 | | } |
388 | | #endif |
389 | | #endif |
390 | 195M | return status; |
391 | 195M | } |
392 | | |
393 | | /* Encode a 1-D scan line. */ |
394 | | /* Attempt to stop coverity thinking skip_white_pixels() taints lbuf:*/ |
395 | | /* coverity[ -tainted_data_return ] */ |
396 | | /* coverity[ -tainted_data_argument arg-2 ] */ |
397 | | static void |
398 | | cf_encode_1d(stream_CFE_state * ss, const byte * lbuf, stream_cursor_write * pw) |
399 | 15.1M | { |
400 | 15.1M | uint count = ss->raster << 3; |
401 | 15.1M | byte *q = pw->ptr; |
402 | 15.1M | int end_count = -ss->Columns & 7; |
403 | 15.1M | int rlen; |
404 | | |
405 | 15.1M | hce_declare_state; |
406 | 15.1M | const byte *p = lbuf; |
407 | 15.1M | byte invert = (ss->BlackIs1 ? 0 : 0xff); |
408 | | |
409 | | /* Invariant: data = p[-1] ^ invert. */ |
410 | 15.1M | uint data = *p++ ^ invert; |
411 | | |
412 | 15.1M | hce_load_state(); |
413 | 232M | while (count != end_count) { |
414 | | /* Parse a white run. */ |
415 | 232M | skip_white_pixels(data, p, count, invert, rlen); |
416 | 232M | CF_PUT_WHITE_RUN(ss, rlen); |
417 | 232M | if (count == end_count) |
418 | 15.0M | break; |
419 | | /* Parse a black run. */ |
420 | 232M | skip_black_pixels(data, p, count, invert, rlen); |
421 | 217M | CF_PUT_BLACK_RUN(ss, rlen); |
422 | 217M | } |
423 | 15.1M | hce_store_state(); |
424 | 15.1M | pw->ptr = q; |
425 | 15.1M | } |
426 | | |
427 | | /* Encode a 2-D scan line. */ |
428 | | /* coverity[ -tainted_data_argument : arg-1 ] */ |
429 | | static void |
430 | | cf_encode_2d(stream_CFE_state * ss, const byte * lbuf, stream_cursor_write * pw, |
431 | | const byte * lprev) |
432 | 65.6M | { |
433 | 65.6M | byte invert_white = (ss->BlackIs1 ? 0 : 0xff); |
434 | 65.6M | byte invert = invert_white; |
435 | 65.6M | uint count = ss->raster << 3; |
436 | 65.6M | int end_count = -ss->Columns & 7; |
437 | 65.6M | const byte *p = lbuf; |
438 | 65.6M | byte *q = pw->ptr; |
439 | 65.6M | uint data = *p++ ^ invert; |
440 | | |
441 | 65.6M | hce_declare_state; |
442 | | /* |
443 | | * In order to handle the nominal 'changing white' at the beginning of |
444 | | * each scan line, we need to suppress the test for an initial black bit |
445 | | * in the reference line when we are at the very beginning of the scan |
446 | | * line. To avoid an extra test, we use two different mask tables. |
447 | | */ |
448 | 65.6M | static const byte initial_count_bit[8] = |
449 | 65.6M | { |
450 | 65.6M | 0, 1, 2, 4, 8, 0x10, 0x20, 0x40 |
451 | 65.6M | }; |
452 | 65.6M | static const byte further_count_bit[8] = |
453 | 65.6M | { |
454 | 65.6M | 0x80, 1, 2, 4, 8, 0x10, 0x20, 0x40 |
455 | 65.6M | }; |
456 | 65.6M | const byte *count_bit = initial_count_bit; |
457 | | |
458 | 65.6M | hce_load_state(); |
459 | 347M | while (count != end_count) { |
460 | | /* |
461 | | * If invert == invert_white, white and black have their |
462 | | * correct meanings; if invert == ~invert_white, |
463 | | * black and white are interchanged. |
464 | | */ |
465 | 281M | uint a0 = count; |
466 | 281M | uint a1; |
467 | | |
468 | 281M | #define b1 (a1 - diff) /* only for printing */ |
469 | 281M | int diff; |
470 | 281M | uint prev_count = count; |
471 | 281M | const byte *prev_p = p - lbuf + lprev; |
472 | 281M | byte prev_data = prev_p[-1] ^ invert; |
473 | 281M | int rlen; |
474 | | |
475 | | /* Find the a1 and b1 transitions. */ |
476 | 281M | skip_white_pixels(data, p, count, invert, rlen); |
477 | 281M | a1 = count; |
478 | 281M | if ((prev_data & count_bit[prev_count & 7])) { |
479 | | /* Look for changing white first. */ |
480 | 35.7M | skip_black_pixels(prev_data, prev_p, prev_count, invert, rlen); |
481 | 35.7M | } |
482 | 281M | count_bit = further_count_bit; /* no longer at beginning */ |
483 | 286M | pass: |
484 | 286M | if (prev_count != end_count) |
485 | 286M | skip_white_pixels(prev_data, prev_p, prev_count, invert, rlen); |
486 | 286M | diff = a1 - prev_count; /* i.e., logical b1 - a1 */ |
487 | | /* In all the comparisons below, remember that count */ |
488 | | /* runs downward, not upward, so the comparisons are */ |
489 | | /* reversed. */ |
490 | 286M | if (diff <= -2) { |
491 | | /* Could be a pass mode. Find b2. */ |
492 | 11.0M | if (prev_count != end_count) |
493 | 11.0M | skip_black_pixels(prev_data, prev_p, |
494 | 11.0M | prev_count, invert, rlen); |
495 | 11.0M | if (prev_count > a1) { |
496 | | /* Use pass mode. */ |
497 | 4.44M | if_debug4m('W', ss->memory, "[W]pass: count = %d, a1 = %d, b1 = %d, new count = %d\n", |
498 | 4.44M | a0, a1, b1, prev_count); |
499 | 4.44M | hc_put_value(ss, q, cf2_run_pass_value, cf2_run_pass_length); |
500 | 4.44M | a0 = prev_count; |
501 | 4.44M | goto pass; |
502 | 4.44M | } |
503 | 11.0M | } |
504 | | /* Check for vertical coding. */ |
505 | 281M | if (diff <= 3 && diff >= -3) { |
506 | | /* Use vertical coding. */ |
507 | 271M | const cfe_run *cp = &cf2_run_vertical[diff + 3]; |
508 | | |
509 | 271M | if_debug5m('W', ss->memory, "[W]vertical %d: count = %d, a1 = %d, b1 = %d, new count = %d\n", |
510 | 271M | diff, a0, a1, b1, count); |
511 | 271M | hc_put_code(ss, q, cp); |
512 | 271M | invert = ~invert; /* a1 polarity changes */ |
513 | 271M | data ^= 0xff; |
514 | 271M | continue; |
515 | 271M | } |
516 | | /* No luck, use horizontal coding. */ |
517 | 10.5M | if (count != end_count) |
518 | 10.5M | skip_black_pixels(data, p, count, invert, rlen); /* find a2 */ |
519 | 10.5M | hc_put_value(ss, q, cf2_run_horizontal_value, |
520 | 10.5M | cf2_run_horizontal_length); |
521 | 10.5M | a0 -= a1; |
522 | 10.5M | a1 -= count; |
523 | 10.5M | if (invert == invert_white) { |
524 | 6.54M | if_debug3m('W', ss->memory, "[W]horizontal: white = %d, black = %d, new count = %d\n", |
525 | 6.54M | a0, a1, count); |
526 | 6.54M | CF_PUT_WHITE_RUN(ss, a0); |
527 | 6.54M | CF_PUT_BLACK_RUN(ss, a1); |
528 | 6.54M | } else { |
529 | 4.03M | if_debug3m('W', ss->memory, "[W]horizontal: black = %d, white = %d, new count = %d\n", |
530 | 4.03M | a0, a1, count); |
531 | 4.03M | CF_PUT_BLACK_RUN(ss, a0); |
532 | 4.03M | CF_PUT_WHITE_RUN(ss, a1); |
533 | 4.03M | #undef b1 |
534 | 4.03M | } |
535 | 10.5M | } |
536 | 65.6M | hce_store_state(); |
537 | 65.6M | pw->ptr = q; |
538 | 65.6M | } |
539 | | |
540 | | /* Stream template */ |
541 | | const stream_template s_CFE_template = |
542 | | { |
543 | | &st_CFE_state, s_CFE_init, s_CFE_process, 1, 1, |
544 | | s_CFE_release, s_CFE_set_defaults |
545 | | }; |