/src/ghostpdl/base/gxclread.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 | | /* Command list reading for Ghostscript. */ |
18 | | #include "memory_.h" |
19 | | #include "gx.h" |
20 | | #include "gp.h" /* for gp_fmode_rb */ |
21 | | #include "gpcheck.h" |
22 | | #include "gserrors.h" |
23 | | #include "gxdevice.h" |
24 | | #include "gscoord.h" /* requires gsmatrix.h */ |
25 | | #include "gsdevice.h" /* for gs_deviceinitialmatrix */ |
26 | | #include "gxdevmem.h" /* must precede gxcldev.h */ |
27 | | #include "gxcldev.h" |
28 | | #include "gxgetbit.h" |
29 | | #include "gxhttile.h" |
30 | | #include "gdevplnx.h" |
31 | | #include "gdevp14.h" |
32 | | #include "gsmemory.h" |
33 | | #include "gsicc_cache.h" |
34 | | /* |
35 | | * We really don't like the fact that gdevprn.h is included here, since |
36 | | * command lists are supposed to be usable for purposes other than printer |
37 | | * devices; but gdev_prn_color_usage and gdev_create_buf_device are |
38 | | * currently only applicable to printer devices. |
39 | | */ |
40 | | #include "gdevprn.h" |
41 | | #include "stream.h" |
42 | | #include "strimpl.h" |
43 | | |
44 | | /* #define EXTRA_OFFSET_MAP_DEBUGGING */ |
45 | | |
46 | | /* forward decl */ |
47 | | private_st_clist_icctable_entry(); |
48 | | private_st_clist_icctable(); |
49 | | |
50 | | /* ------ Band file reading stream ------ */ |
51 | | |
52 | | #ifdef DEBUG |
53 | | /* An auxiliary table for mapping clist buffer offsets to cfile offsets. */ |
54 | | typedef struct { |
55 | | uint buffered; |
56 | | int64_t file_offset; |
57 | | } cbuf_offset_map_elem; |
58 | | #endif |
59 | | |
60 | | /* |
61 | | * To separate banding per se from command list interpretation, |
62 | | * we make the command list interpreter simply read from a stream. |
63 | | * When we are actually doing banding, the stream filters the band file |
64 | | * and only passes through the commands for the current bands (or band |
65 | | * ranges that include a current band). |
66 | | */ |
67 | | typedef struct stream_band_read_state_s { |
68 | | stream_state_common; |
69 | | gx_band_page_info_t page_info; |
70 | | int band_first, band_last; |
71 | | uint left; /* amount of data left in this run */ |
72 | | cmd_block b_this; |
73 | | gs_memory_t *local_memory; |
74 | | #ifdef DEBUG |
75 | | bool skip_first; |
76 | | cbuf_offset_map_elem *offset_map; |
77 | | int bytes_skipped; |
78 | | int offset_map_length; |
79 | | int offset_map_max_length; |
80 | | int skip_next; |
81 | | #endif |
82 | | } stream_band_read_state; |
83 | | |
84 | | static int |
85 | | s_band_read_init(stream_state * st) |
86 | 3.00M | { |
87 | 3.00M | stream_band_read_state *const ss = (stream_band_read_state *) st; |
88 | 3.00M | const clist_io_procs_t *io_procs = ss->page_info.io_procs; |
89 | | |
90 | 3.00M | ss->left = 0; |
91 | 3.00M | ss->b_this.band_min = 0; |
92 | 3.00M | ss->b_this.band_max = 0; |
93 | 3.00M | ss->b_this.pos = 0; |
94 | 3.00M | return io_procs->rewind(ss->page_info.bfile, false, ss->page_info.bfname); |
95 | 3.00M | } |
96 | | |
97 | | #ifdef DEBUG |
98 | | static int |
99 | | s_band_read_init_offset_map(gx_device_clist_reader *crdev, stream_state * st) |
100 | | { |
101 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
102 | | |
103 | | if (gs_debug_c('L')) { |
104 | | ss->offset_map_length = 0; |
105 | | ss->offset_map_max_length = cbuf_size + 1; /* fixme: Wanted a more accurate implementation. */ |
106 | | ss->offset_map = (cbuf_offset_map_elem *)gs_alloc_byte_array(crdev->memory, |
107 | | ss->offset_map_max_length, sizeof(*ss->offset_map), "s_band_read_init_offset_map"); |
108 | | if (ss->offset_map == NULL) |
109 | | return_error(gs_error_VMerror); |
110 | | ss->offset_map[0].buffered = 0; |
111 | | ss->bytes_skipped = 0; |
112 | | crdev->offset_map = ss->offset_map; /* Prevent collecting it as garbage. |
113 | | Debugged with ppmraw -r300 014-09.ps . */ |
114 | | } else { |
115 | | ss->offset_map_length = 0; |
116 | | ss->offset_map_max_length = 0; |
117 | | ss->offset_map = NULL; |
118 | | ss->bytes_skipped = 0; |
119 | | crdev->offset_map = NULL; |
120 | | } |
121 | | ss->skip_first = true; |
122 | | ss->skip_next = 0; |
123 | | return 0; |
124 | | } |
125 | | |
126 | | static void |
127 | | s_band_read_dnit_offset_map(gx_device_clist_reader *crdev, stream_state * st) |
128 | | { |
129 | | if (gs_debug_c('L')) { |
130 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
131 | | |
132 | | gs_free_object(crdev->memory, ss->offset_map, "s_band_read_dnit_offset_map"); |
133 | | crdev->offset_map = 0; |
134 | | } |
135 | | } |
136 | | #endif |
137 | | |
138 | | static int |
139 | | s_band_read_process(stream_state * st, stream_cursor_read * ignore_pr, |
140 | | stream_cursor_write * pw, bool last) |
141 | 41.8M | { |
142 | 41.8M | stream_band_read_state *const ss = (stream_band_read_state *) st; |
143 | 41.8M | register byte *q = pw->ptr; |
144 | 41.8M | byte *wlimit = pw->limit; |
145 | 41.8M | clist_file_ptr cfile = ss->page_info.cfile; |
146 | 41.8M | clist_file_ptr bfile = ss->page_info.bfile; |
147 | 41.8M | uint left = ss->left; |
148 | 41.8M | int status = 1; |
149 | 41.8M | uint count; |
150 | 41.8M | const clist_io_procs_t *io_procs = ss->page_info.io_procs; |
151 | 41.8M | int64_t pos; |
152 | | |
153 | | /* left = number of bytes unread in the current command. */ |
154 | | /* count = number of bytes we have room in our buffer for. */ |
155 | 375M | while ((count = wlimit - q) != 0) { |
156 | 336M | int bmin, bmax; |
157 | | /* If there is more data to be read in the current command, then pull that in. */ |
158 | 336M | if (left) { |
159 | 185M | if (count > left) |
160 | 147M | count = left; |
161 | | #ifdef DEBUG |
162 | | if (gs_debug_c('L')) { |
163 | | if (ss->skip_next) { |
164 | | /* This buffer fill is NOT going into the normal buffer. */ |
165 | | ss->skip_next = 0; |
166 | | ss->bytes_skipped += count; |
167 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
168 | | if (ss->offset_map_length != 1) { |
169 | | dmlprintf(ss->local_memory, "offset_map: confused!\n"); |
170 | | exit(1); |
171 | | } |
172 | | #endif |
173 | | } else { |
174 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
175 | | if (ss->offset_map[ss->offset_map_length - 1].buffered + count > cbuf_size*2) { |
176 | | dmlprintf2(ss->local_memory, "Invalid update to buffered. %d %d\n", |
177 | | ss->offset_map[ss->offset_map_length - 1].buffered, count); |
178 | | exit(1); |
179 | | } |
180 | | #endif |
181 | | ss->offset_map[ss->offset_map_length - 1].buffered += count; |
182 | | } |
183 | | } |
184 | | #endif |
185 | 185M | io_procs->fread_chars(q + 1, count, cfile); |
186 | 185M | if (io_procs->ferror_code(cfile) < 0) { |
187 | 0 | status = ERRC; |
188 | 0 | break; |
189 | 0 | } |
190 | 185M | q += count; |
191 | 185M | left -= count; |
192 | 185M | process_interrupts(ss->local_memory); |
193 | 185M | continue; |
194 | 185M | } |
195 | | /* The current command is over. So find the next command in the bfile |
196 | | * that applies to the current band(s) and read that in. */ |
197 | 2.79G | do { |
198 | 2.79G | int nread; |
199 | | /* If we hit eof, end! */ |
200 | | /* Could this test be moved into the nread < sizeof() test below? */ |
201 | 2.79G | if (ss->b_this.band_min == cmd_band_end && |
202 | 2.79G | io_procs->ftell(bfile) == ss->page_info.bfile_end_pos) { |
203 | 2.99M | pw->ptr = q; |
204 | 2.99M | ss->left = left; |
205 | 2.99M | return EOFC; |
206 | 2.99M | } |
207 | | |
208 | | /* Read the next cmd_block from the bfile. Each cmd_block contains |
209 | | * the bands to use, and the file position of the END of the data. |
210 | | * We therefore want to read the data from the file position given |
211 | | * in the PREVIOUS record onwards, and compare to the band min/max |
212 | | * given there too. */ |
213 | 2.79G | bmin = ss->b_this.band_min; |
214 | 2.79G | bmax = ss->b_this.band_max; |
215 | 2.79G | pos = ss->b_this.pos; /* Record where our data starts! */ |
216 | 2.79G | nread = io_procs->fread_chars(&ss->b_this, sizeof(ss->b_this), bfile); |
217 | 2.79G | if (nread < sizeof(ss->b_this)) { |
218 | 0 | DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */ |
219 | 0 | return ERRC; |
220 | 0 | } |
221 | 2.79G | } while (ss->band_last < bmin || ss->band_first > bmax); |
222 | | /* So let's set up to read the actual command data from cfile. Seek... */ |
223 | 147M | io_procs->fseek(cfile, pos, SEEK_SET, ss->page_info.cfname); |
224 | 147M | left = (uint) (ss->b_this.pos - pos); |
225 | | #ifdef DEBUG |
226 | | if (left > 0 && gs_debug_c('L')) { |
227 | | if (ss->offset_map_length >= ss->offset_map_max_length) { |
228 | | DISCARD(gs_note_error(gs_error_unregistered)); /* Must not happen. */ |
229 | | return ERRC; |
230 | | } |
231 | | ss->offset_map[ss->offset_map_length].file_offset = pos; |
232 | | ss->offset_map[ss->offset_map_length].buffered = 0; |
233 | | ss->offset_map_length++; |
234 | | } |
235 | | #endif |
236 | 147M | if_debug5m('l', ss->local_memory, |
237 | 147M | "[l]reading for bands (%d,%d) at bfile %"PRId64", cfile %"PRId64", length %u\n", |
238 | 147M | bmin, bmax, |
239 | 147M | (io_procs->ftell(bfile) - sizeof(ss->b_this)), (int64_t)pos, left); |
240 | 147M | } |
241 | 38.8M | pw->ptr = q; |
242 | 38.8M | ss->left = left; |
243 | 38.8M | return status; |
244 | 41.8M | } |
245 | | |
246 | | /* Stream template */ |
247 | | static const stream_template s_band_read_template = { |
248 | | &st_stream_state, s_band_read_init, s_band_read_process, 1, cbuf_size |
249 | | }; |
250 | | |
251 | | #ifdef DEBUG |
252 | | /* In DEBUG builds, we maintain an "offset_map" within stream_band_read_state, |
253 | | * that allows us to relate offsets within the buffer, to offsets within the |
254 | | * cfile. |
255 | | * |
256 | | * At any given point, for stream_band_read_state *ss: |
257 | | * There are n = ss->offset_map_length records in the table. |
258 | | * offset = 0; |
259 | | * for (i = 0; i < n; i++) |
260 | | * // Offset 'offset' in the buffer corresponds to ss->offset_map[i].file_offset in the file. |
261 | | * offset += ss->offset_map[i].buffered |
262 | | * |
263 | | * As we pull data from the stream, we keep file_offset and buffered up to date. Note that |
264 | | * there are 2 cbuf_size sized buffers in play here. The cmd_buffer has one cbuf_size sized |
265 | | * buffer in it. Data is pulled into that from the stream, which has another cbuf_sized |
266 | | * buffer into it. Accordingly, 'buffered' should never be > 2*cbuf_size = 8192. |
267 | | * |
268 | | * Sometimes we will pull data out of the stream, bypassing the cmd_buffer's buffer. In this |
269 | | * case, we 'skip' data, and record the number of bytes skipped in ss->bytes_skipped. This |
270 | | * should only ever happen when we have already advanced as much as possible (i.e. when the |
271 | | * current offset is in the first record). |
272 | | */ |
273 | | |
274 | | /* Given buffer_offset (an offset within the buffer), return the number of the offset_map |
275 | | * record that contains it. Also fill poffset0 in with the offset of the start of that |
276 | | * record within the buffer. (NOTE, depending on how much of the record has already been |
277 | | * read, some bytes may already have been lost). */ |
278 | | static int |
279 | | buffer_segment_index(const stream_band_read_state *ss, uint buffer_offset, uint *poffset0) |
280 | | { |
281 | | uint i, offset0, offset = 0; |
282 | | |
283 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
284 | | dmlprintf1(ss->local_memory, "buffer_segment_index: buffer_offset=%d\n", buffer_offset); |
285 | | for (i = 0; i < ss->offset_map_length; i++) { |
286 | | dmlprintf3(ss->local_memory, " offset_map[%d].file_offset=%"PRId64" buffered=%d\n", i, ss->offset_map[i].file_offset, ss->offset_map[i].buffered); |
287 | | } |
288 | | #endif |
289 | | for (i = 0; i < ss->offset_map_length; i++) { |
290 | | offset0 = offset; |
291 | | offset += ss->offset_map[i].buffered; |
292 | | if (buffer_offset < offset) { |
293 | | *poffset0 = offset0; |
294 | | return i; |
295 | | } |
296 | | } |
297 | | /* Now cope with the case where we've read exactly to the end of the buffer. |
298 | | * There might be more data still to come. */ |
299 | | if (buffer_offset == offset) { |
300 | | *poffset0 = offset0; |
301 | | return i-1; |
302 | | } |
303 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
304 | | dmlprintf1(ss->local_memory, "buffer_segment_index fail: buffer_offset=%d not found\n", buffer_offset); |
305 | | exit(1); |
306 | | #else |
307 | | (void)gs_note_error(gs_error_unregistered); /* Must not happen. */ |
308 | | #endif |
309 | | return -1; |
310 | | } |
311 | | |
312 | | /* Map from a buffer offset, to the offset of the corresponding byte in the |
313 | | * cfile. */ |
314 | | int64_t |
315 | | clist_file_offset(const stream_state * st, uint buffer_offset) |
316 | | { |
317 | | const stream_band_read_state *ss = (const stream_band_read_state *) st; |
318 | | uint offset0; |
319 | | int i = buffer_segment_index(ss, buffer_offset, &offset0); |
320 | | |
321 | | if (i < 0) |
322 | | return 0; |
323 | | |
324 | | return ss->offset_map[i].file_offset + (uint)(buffer_offset - offset0); |
325 | | } |
326 | | |
327 | | void |
328 | | top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end) |
329 | | { |
330 | | /* NOTE: The clist data are buffered in the clist reader buffer and in the |
331 | | internal buffer of the clist stream. Since the 1st buffer is not accessible |
332 | | from s_band_read_process, offset_map corresponds the union of the 2 buffers. |
333 | | */ |
334 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
335 | | uint buffer_offset, offset0, consumed; |
336 | | int i; |
337 | | |
338 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
339 | | if (ptr < buf || end < ptr || end < buf || end > buf + cbuf_size) |
340 | | { |
341 | | dmlprintf3(ss->local_memory, "Invalid pointers for top_up_offset_map: buf=%p ptr=%p end=%p\n", buf, ptr, end); |
342 | | } |
343 | | #endif |
344 | | |
345 | | if (!gs_debug_c('L')) |
346 | | return; |
347 | | if (ss->skip_first) { |
348 | | /* Work around the trick with initializing the buffer pointer with the buffer end. */ |
349 | | ss->skip_first = false; |
350 | | return; |
351 | | } |
352 | | if (ptr == buf) |
353 | | return; |
354 | | |
355 | | /* We know that buf <= ptr <= end <= buf+4096, so uint is quite enough! */ |
356 | | buffer_offset = ptr - buf; |
357 | | i = buffer_segment_index(ss, buffer_offset, &offset0); |
358 | | |
359 | | consumed = buffer_offset - offset0; |
360 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
361 | | dmlprintf3(ss->local_memory, "offset_map: dump %d entries + %d bytes + %d skipped bytes\n", i, consumed, ss->bytes_skipped); |
362 | | if (ss->offset_map[i].buffered < consumed) { |
363 | | dmlprintf2(ss->local_memory, "Invalid update to buffered. B %d %d\n", ss->offset_map[i].buffered, consumed); |
364 | | exit(1); |
365 | | } |
366 | | #endif |
367 | | ss->offset_map[i].buffered -= consumed; |
368 | | ss->offset_map[i].file_offset += consumed; |
369 | | ss->bytes_skipped = 0; |
370 | | if (i) { |
371 | | memmove(ss->offset_map, ss->offset_map + i, |
372 | | (ss->offset_map_length - i) * sizeof(*ss->offset_map)); |
373 | | ss->offset_map_length -= i; |
374 | | } |
375 | | } |
376 | | |
377 | | /* This function is called when data is copied from the stream out into a separate |
378 | | * buffer without going through the usual clist buffers. Essentially data for the |
379 | | * id we are reading at buffer_offset within the buffer is skipped. */ |
380 | | void adjust_offset_map_for_skipped_data(stream_state *st, uint buffer_offset, uint skipped) |
381 | | { |
382 | | uint offset0; |
383 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
384 | | int i; |
385 | | |
386 | | if (!gs_debug_c('L')) |
387 | | return; |
388 | | |
389 | | i = buffer_segment_index(ss, buffer_offset, &offset0); |
390 | | |
391 | | ss->offset_map[i].buffered -= skipped; |
392 | | ss->offset_map[i].file_offset += skipped; |
393 | | } |
394 | | |
395 | | void |
396 | | offset_map_next_data_out_of_band(stream_state *st) |
397 | | { |
398 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
399 | | |
400 | | if (!gs_debug_c('L')) |
401 | | return; |
402 | | |
403 | | ss->skip_next = 1; |
404 | | } |
405 | | #endif /* DEBUG */ |
406 | | |
407 | | /* ------ Reading/rendering ------ */ |
408 | | |
409 | | /* Calculate the raster for a chunky or planar device. */ |
410 | | static int |
411 | | clist_plane_raster(const gx_device *dev, const gx_render_plane_t *render_plane) |
412 | 160M | { |
413 | 160M | return gx_device_raster_plane(dev, render_plane); |
414 | 160M | } |
415 | | |
416 | | /* Select full-pixel rendering if required for RasterOp. */ |
417 | | void |
418 | | clist_select_render_plane(gx_device *dev, int y, int height, |
419 | | gx_render_plane_t *render_plane, int index) |
420 | 160M | { |
421 | 160M | if (index >= 0) { |
422 | 0 | gx_color_usage_t color_usage; |
423 | 0 | int ignore_start; |
424 | |
|
425 | 0 | gdev_prn_color_usage(dev, y, height, &color_usage, &ignore_start); |
426 | 0 | if (color_usage.slow_rop) |
427 | 0 | index = -1; |
428 | 0 | } |
429 | 160M | if (index < 0) |
430 | 160M | render_plane->index = index; |
431 | 0 | else |
432 | 0 | gx_render_plane_init(render_plane, dev, index); |
433 | 160M | } |
434 | | |
435 | | /* |
436 | | * Do device setup from params stored in command list. This is only for |
437 | | * async rendering & assumes that the first command in every command list |
438 | | * is a put_params command which sets all space-related parameters to the |
439 | | * value they will have for the duration of that command list. |
440 | | */ |
441 | | int |
442 | | clist_setup_params(gx_device *dev) |
443 | 0 | { |
444 | 0 | gx_device_clist *cldev = (gx_device_clist *)dev; |
445 | 0 | gx_device_clist_reader * const crdev = &cldev->reader; |
446 | 0 | int code = clist_render_init(cldev); |
447 | |
|
448 | 0 | if (code < 0) |
449 | 0 | return code; |
450 | | |
451 | 0 | code = clist_playback_file_bands(playback_action_setup, |
452 | 0 | crdev, &crdev->page_info, 0, 0, 0, 0, 0); |
453 | | |
454 | | /* put_params may have reinitialized device into a writer */ |
455 | 0 | clist_render_init(cldev); |
456 | |
|
457 | 0 | return code; |
458 | 0 | } |
459 | | |
460 | | int |
461 | | clist_close_writer_and_init_reader(gx_device_clist *cldev) |
462 | 160M | { |
463 | 160M | gx_device_clist_reader * const crdev = &cldev->reader; |
464 | 160M | gs_memory_t *base_mem = crdev->memory->thread_safe_memory; |
465 | 160M | gs_memory_status_t mem_status; |
466 | 160M | int code = 0; |
467 | | |
468 | | /* Initialize for rendering if we haven't done so yet. */ |
469 | 160M | if (crdev->ymin < 0) { |
470 | 777k | code = clist_end_page(&cldev->writer); |
471 | 777k | if (code < 0) |
472 | 0 | return code; |
473 | 777k | code = clist_render_init(cldev); |
474 | 777k | if (code < 0) |
475 | 0 | return code; |
476 | | /* allocate and load the color_usage_array */ |
477 | 777k | code = clist_read_color_usage_array(crdev); |
478 | 777k | if (code < 0) |
479 | 0 | return code; |
480 | | /* Check for and get ICC profile table */ |
481 | 777k | code = clist_read_icctable(crdev); |
482 | 777k | if (code < 0) |
483 | 0 | return code; |
484 | | /* Allocate the icc cache for the clist reader */ |
485 | | /* Since we may be rendering in multiple threads, make sure the memory */ |
486 | | /* is thread safe by using a known thread_safe memory allocator */ |
487 | 777k | gs_memory_status(base_mem, &mem_status); |
488 | 777k | if (mem_status.is_thread_safe == false) { |
489 | 0 | return_error(gs_error_VMerror); |
490 | 0 | } |
491 | | |
492 | 777k | if (crdev->icc_cache_cl == NULL) { |
493 | 237k | code = (crdev->icc_cache_cl = gsicc_cache_new(base_mem)) == NULL ? gs_error_VMerror : code; |
494 | 237k | } |
495 | 777k | } |
496 | | |
497 | 160M | check_device_compatible_encoding((gx_device *)cldev); |
498 | | |
499 | 160M | return code; |
500 | 160M | } |
501 | | |
502 | | /* Used to find the command block information in the bfile |
503 | | that is related to extra information stored in a psuedo band. |
504 | | Currently application of this is storage of the ICC profile |
505 | | table, the per-band color_usage array, and the spot equivalent |
506 | | colors when doing overprint simulation. We may eventually |
507 | | use this for storing other information like compressed images. */ |
508 | | |
509 | | static int |
510 | | clist_find_pseudoband(gx_device_clist_reader *crdev, int band, cmd_block *cb) |
511 | 1.59M | { |
512 | | |
513 | 1.59M | gx_band_page_info_t *page_info = &(crdev->page_info); |
514 | 1.59M | clist_file_ptr bfile = page_info->bfile; |
515 | 1.59M | int64_t save_pos = page_info->bfile_end_pos; |
516 | 1.59M | int64_t start_pos; |
517 | 1.59M | int code; |
518 | | |
519 | 1.59M | if (bfile == NULL) { |
520 | | /* files haven't been opened yet. Do it now */ |
521 | 0 | char fmode[4]; |
522 | |
|
523 | 0 | strcpy(fmode, "r"); |
524 | 0 | strncat(fmode, gp_fmode_binary_suffix, 1); |
525 | 0 | if ((code=page_info->io_procs->fopen(page_info->cfname, fmode, |
526 | 0 | &page_info->cfile, |
527 | 0 | crdev->memory, crdev->memory, true)) < 0 || |
528 | 0 | (code=page_info->io_procs->fopen(page_info->bfname, fmode, |
529 | 0 | &page_info->bfile, |
530 | 0 | crdev->memory, crdev->memory, false)) < 0) { |
531 | 0 | return code; |
532 | 0 | } |
533 | 0 | bfile = page_info->bfile; |
534 | 0 | } |
535 | | /* Go to the start of the last command block */ |
536 | 1.59M | start_pos = page_info->bfile_end_pos - sizeof(cmd_block); |
537 | 1.59M | page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname); |
538 | 34.5M | while( 1 ) { |
539 | 34.5M | int read = page_info->io_procs->fread_chars(cb, sizeof(cmd_block), bfile); |
540 | | |
541 | 34.5M | if (read < sizeof(cmd_block)) |
542 | 0 | return -1; |
543 | 34.5M | if (cb->band_max == band && cb->band_min == band) { |
544 | 793k | page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname); |
545 | 793k | return(0); /* Found it */ |
546 | 793k | } |
547 | 33.7M | start_pos -= sizeof(cmd_block); |
548 | 33.7M | if (start_pos < 0) { |
549 | 797k | page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname); |
550 | 797k | return(-1); /* Did not find it before getting into other stuff in normal bands */ |
551 | 797k | } |
552 | 32.9M | page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname); |
553 | 32.9M | } |
554 | 1.59M | } |
555 | | |
556 | | /* A procedure to read a chunk of data from the cfile at a particular location into buff */ |
557 | | int |
558 | | clist_read_chunk(gx_device_clist_reader *crdev, int64_t position, int size, unsigned char *buf) |
559 | 2.31M | { |
560 | 2.31M | clist_file_ptr cfile = crdev->page_info.cfile; |
561 | 2.31M | int64_t save_pos; |
562 | | |
563 | | /* Save our current location */ |
564 | 2.31M | save_pos = crdev->page_info.io_procs->ftell(cfile); |
565 | | /* Go to our new position */ |
566 | 2.31M | crdev->page_info.io_procs->fseek(cfile, position, SEEK_SET, crdev->page_info.cfname); |
567 | | /* Get the data */ |
568 | 2.31M | crdev->page_info.io_procs->fread_chars(buf, size, cfile); |
569 | | /* Restore our position */ |
570 | 2.31M | crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname); |
571 | 2.31M | return 0; |
572 | 2.31M | } |
573 | | |
574 | | /* read the color_usage_array back from the pseudo band */ |
575 | | int |
576 | | clist_read_color_usage_array(gx_device_clist_reader *crdev) |
577 | 777k | { |
578 | 777k | int code, size_data = crdev->nbands * sizeof(gx_color_usage_t ); |
579 | 777k | cmd_block cb; |
580 | | |
581 | 777k | if (crdev->color_usage_array != NULL) |
582 | 0 | gs_free_object(crdev->memory, crdev->color_usage_array, |
583 | 777k | "clist reader color_usage_array"); |
584 | 777k | crdev->color_usage_array = (gx_color_usage_t *)gs_alloc_bytes(crdev->memory, size_data, |
585 | 777k | "clist reader color_usage_array"); |
586 | 777k | if (crdev->color_usage_array == NULL) |
587 | 0 | return_error(gs_error_VMerror); |
588 | | |
589 | 777k | code = clist_find_pseudoband(crdev, crdev->nbands + COLOR_USAGE_OFFSET - 1, &cb); |
590 | 777k | if (code < 0) |
591 | 0 | return code; |
592 | | |
593 | 777k | code = clist_read_chunk(crdev, cb.pos, size_data, (unsigned char *)crdev->color_usage_array); |
594 | 777k | return code; |
595 | 777k | } |
596 | | |
597 | | /* read the cmyk equivalent spot colors */ |
598 | | int |
599 | | clist_read_op_equiv_cmyk_colors(gx_device_clist_reader *crdev, |
600 | | equivalent_cmyk_color_params *op_equiv_cmyk_colors) |
601 | 0 | { |
602 | 0 | int code; |
603 | 0 | cmd_block cb; |
604 | |
|
605 | 0 | code = clist_find_pseudoband(crdev, crdev->nbands + SPOT_EQUIV_COLORS - 1, &cb); |
606 | 0 | if (code < 0) |
607 | 0 | return code; |
608 | | |
609 | 0 | code = clist_read_chunk(crdev, cb.pos, sizeof(equivalent_cmyk_color_params), |
610 | 0 | (unsigned char *)op_equiv_cmyk_colors); |
611 | 0 | return code; |
612 | 0 | } |
613 | | |
614 | | /* Unserialize the icc table information stored in the cfile and |
615 | | place it in the reader device */ |
616 | | static int |
617 | | clist_unserialize_icctable(gx_device_clist_reader *crdev, cmd_block *cb) |
618 | 15.5k | { |
619 | 15.5k | clist_file_ptr cfile = crdev->page_info.cfile; |
620 | 15.5k | clist_icctable_t *icc_table = crdev->icc_table; |
621 | 15.5k | int64_t save_pos; |
622 | 15.5k | int number_entries, size_data; |
623 | 15.5k | unsigned char *buf, *buf_start; |
624 | 15.5k | clist_icctable_entry_t *curr_entry; |
625 | 15.5k | int k; |
626 | 15.5k | gs_memory_t *stable_mem = crdev->memory->stable_memory; |
627 | | |
628 | 15.5k | if ( icc_table != NULL ) |
629 | 0 | return(0); |
630 | 15.5k | save_pos = crdev->page_info.io_procs->ftell(cfile); |
631 | 15.5k | crdev->page_info.io_procs->fseek(cfile, cb->pos, SEEK_SET, crdev->page_info.cfname); |
632 | | /* First four bytes tell us the number of entries. */ |
633 | 15.5k | crdev->page_info.io_procs->fread_chars(&number_entries, sizeof(number_entries), cfile); |
634 | | /* Allocate the space */ |
635 | 15.5k | size_data = number_entries*sizeof(clist_icc_serial_entry_t); |
636 | 15.5k | buf = gs_alloc_bytes(crdev->memory, size_data, "clist_read_icctable"); |
637 | 15.5k | buf_start = buf; |
638 | 15.5k | if (buf == NULL) |
639 | 0 | return gs_rethrow(-1, "insufficient memory for icc table buffer reader"); |
640 | | /* Get the data */ |
641 | 15.5k | clist_read_chunk(crdev, cb->pos + 4, size_data, buf); |
642 | 15.5k | icc_table = gs_alloc_struct(stable_mem, clist_icctable_t, |
643 | 15.5k | &st_clist_icctable, "clist_read_icctable"); |
644 | 15.5k | if (icc_table == NULL) { |
645 | 0 | gs_free_object(stable_mem, buf_start, "clist_read_icctable"); |
646 | 0 | return gs_rethrow(-1, "insufficient memory for icc table buffer reader"); |
647 | 0 | } |
648 | 15.5k | icc_table->memory = stable_mem; |
649 | 15.5k | icc_table->head = NULL; |
650 | 15.5k | icc_table->final = NULL; |
651 | | /* Allocate and fill each entry */ |
652 | 15.5k | icc_table->tablesize = number_entries; |
653 | 15.5k | crdev->icc_table = icc_table; |
654 | 35.5k | for (k = 0; k < number_entries; k++) { |
655 | 20.0k | curr_entry = gs_alloc_struct(stable_mem, clist_icctable_entry_t, |
656 | 20.0k | &st_clist_icctable_entry, "clist_read_icctable"); |
657 | 20.0k | if (curr_entry == NULL) { |
658 | 0 | gs_free_object(stable_mem, buf_start, "clist_read_icctable"); |
659 | 0 | return gs_rethrow(-1, "insufficient memory for icc table entry"); |
660 | 0 | } |
661 | 20.0k | memcpy(&(curr_entry->serial_data), buf, sizeof(clist_icc_serial_entry_t)); |
662 | 20.0k | buf += sizeof(clist_icc_serial_entry_t); |
663 | 20.0k | curr_entry->icc_profile = NULL; |
664 | 20.0k | if ( icc_table->head == NULL ) { |
665 | 15.5k | icc_table->head = curr_entry; |
666 | 15.5k | icc_table->final = curr_entry; |
667 | 15.5k | } else { |
668 | 4.47k | icc_table->final->next = curr_entry; |
669 | 4.47k | icc_table->final = curr_entry; |
670 | 4.47k | } |
671 | 20.0k | curr_entry->next = NULL; |
672 | 20.0k | } |
673 | 15.5k | gs_free_object(crdev->memory, buf_start, "clist_read_icctable"); |
674 | 15.5k | crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname); |
675 | 15.5k | return 0; |
676 | 15.5k | } |
677 | | |
678 | | /* Get the ICC profile table information from the clist */ |
679 | | int |
680 | | clist_read_icctable(gx_device_clist_reader *crdev) |
681 | 812k | { |
682 | | /* Look for the command block of the ICC Profile. */ |
683 | 812k | cmd_block cb; |
684 | 812k | int code; |
685 | | |
686 | | /* First get the command block which will tell us where the |
687 | | information is stored in the cfile */ |
688 | 812k | code = clist_find_pseudoband(crdev, crdev->nbands + ICC_TABLE_OFFSET - 1, &cb); |
689 | 812k | if (code < 0) |
690 | 797k | return(0); /* No ICC information */ |
691 | | /* Unserialize the icc_table from the cfile */ |
692 | 15.5k | code = clist_unserialize_icctable(crdev, &cb); |
693 | 15.5k | return(code); |
694 | 812k | } |
695 | | |
696 | | /* Initialize for reading. */ |
697 | | int |
698 | | clist_render_init(gx_device_clist *dev) |
699 | 812k | { |
700 | 812k | gx_device_clist_reader * const crdev = &dev->reader; |
701 | | |
702 | 812k | crdev->ymin = crdev->ymax = 0; |
703 | 812k | crdev->yplane.index = -1; |
704 | | /* For normal rasterizing, pages and num_pages is 1. */ |
705 | 812k | crdev->pages = 0; |
706 | 812k | crdev->num_pages = 1; /* always at least one page */ |
707 | 812k | crdev->offset_map = NULL; |
708 | 812k | crdev->icc_table = NULL; |
709 | 812k | crdev->color_usage_array = NULL; |
710 | 812k | crdev->render_threads = NULL; |
711 | | |
712 | 812k | return 0; |
713 | 812k | } |
714 | | |
715 | | /* Copy a rasterized rectangle to the client, rasterizing if needed. */ |
716 | | int |
717 | | clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect, |
718 | | gs_get_bits_params_t *params) |
719 | 160M | { |
720 | 160M | gx_device_clist *cldev = (gx_device_clist *)dev; |
721 | 160M | gx_device_clist_reader *crdev = &cldev->reader; |
722 | 160M | gx_device_clist_common *cdev = (gx_device_clist_common *)dev; |
723 | 160M | gs_get_bits_options_t options = params->options; |
724 | 160M | int y = prect->p.y; |
725 | 160M | int end_y = prect->q.y; |
726 | 160M | int line_count = end_y - y; |
727 | 160M | gs_int_rect band_rect; |
728 | 160M | int lines_rasterized; |
729 | 160M | gx_device *bdev; |
730 | 160M | uint num_planes = |
731 | 160M | (options & GB_PACKING_CHUNKY ? 1 : |
732 | 160M | options & GB_PACKING_PLANAR ? dev->color_info.num_components : |
733 | 7.39M | options & GB_PACKING_BIT_PLANAR ? dev->color_info.depth : |
734 | 0 | 0 /****** NOT POSSIBLE ******/); |
735 | 160M | gx_render_plane_t render_plane; |
736 | 160M | int plane_index; |
737 | 160M | int my; |
738 | 160M | int code; |
739 | | |
740 | 160M | if (prect->p.x < 0 || prect->q.x > dev->width || |
741 | 160M | y < 0 || end_y > dev->height |
742 | 160M | ) |
743 | 0 | return_error(gs_error_rangecheck); |
744 | 160M | if (line_count <= 0 || prect->p.x >= prect->q.x) |
745 | 8.06k | return 0; |
746 | | |
747 | | /* |
748 | | * Calculate the render_plane from the params. There are two cases: |
749 | | * full pixels, or a single plane. |
750 | | */ |
751 | 160M | plane_index = -1; |
752 | 160M | if (options & GB_SELECT_PLANES) { |
753 | | /* Look for the one selected plane. */ |
754 | 0 | int i; |
755 | |
|
756 | 0 | for (i = 0; i < num_planes; ++i) |
757 | 0 | if (params->data[i]) { |
758 | 0 | if (plane_index >= 0) /* >1 plane requested */ |
759 | 0 | return gx_default_get_bits_rectangle(dev, prect, params); |
760 | 0 | plane_index = i; |
761 | 0 | } |
762 | 0 | } |
763 | | |
764 | 160M | if (0 > (code = clist_close_writer_and_init_reader(cldev))) |
765 | 0 | return code; |
766 | | |
767 | 160M | clist_select_render_plane(dev, y, line_count, &render_plane, plane_index); |
768 | 160M | code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, |
769 | 160M | &bdev, cdev->target, y, &render_plane, |
770 | 160M | dev->memory, |
771 | 160M | &(crdev->color_usage_array[y/crdev->page_info.band_params.BandHeight])); |
772 | 160M | if (code < 0) |
773 | 0 | return code; |
774 | 160M | code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my); |
775 | 160M | if (code >= 0) { |
776 | 160M | lines_rasterized = min(code, line_count); |
777 | | /* Return as much of the rectangle as falls within the rasterized lines. */ |
778 | 160M | band_rect = *prect; |
779 | 160M | band_rect.p.y = my; |
780 | 160M | band_rect.q.y = my + lines_rasterized; |
781 | 160M | code = dev_proc(bdev, get_bits_rectangle) |
782 | 160M | (bdev, &band_rect, params); |
783 | 160M | } |
784 | 160M | cdev->buf_procs.destroy_buf_device(bdev); |
785 | 160M | if (code < 0 || lines_rasterized == line_count) |
786 | 160M | return code; |
787 | | /* |
788 | | * We'll have to return the rectangle in pieces. Force GB_RETURN_COPY |
789 | | * rather than GB_RETURN_POINTER, and require all subsequent pieces to |
790 | | * use the same values as the first piece for all of the other format |
791 | | * options. If copying isn't allowed, or if there are any unread |
792 | | * rectangles, punt. |
793 | | */ |
794 | 0 | if (!(options & GB_RETURN_COPY) || code > 0) |
795 | 0 | return gx_default_get_bits_rectangle(dev, prect, params); |
796 | 0 | options = params->options; |
797 | 0 | if (!(options & GB_RETURN_COPY)) { |
798 | | /* Redo the first piece with copying. */ |
799 | 0 | params->options = options = |
800 | 0 | (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY; |
801 | 0 | lines_rasterized = 0; |
802 | 0 | } |
803 | 0 | { |
804 | 0 | gs_get_bits_params_t band_params; |
805 | 0 | uint raster = gx_device_raster(bdev, true); |
806 | |
|
807 | 0 | code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, |
808 | 0 | &bdev, cdev->target, y, &render_plane, |
809 | 0 | dev->memory, |
810 | 0 | &(crdev->color_usage_array[y/crdev->page_info.band_params.BandHeight])); |
811 | 0 | if (code < 0) |
812 | 0 | return code; |
813 | 0 | band_params = *params; |
814 | 0 | while ((y += lines_rasterized) < end_y) { |
815 | 0 | int i; |
816 | | |
817 | | /* Increment data pointers by lines_rasterized. */ |
818 | 0 | for (i = 0; i < num_planes; ++i) |
819 | 0 | if (band_params.data[i]) |
820 | 0 | band_params.data[i] += raster * lines_rasterized; |
821 | 0 | line_count = end_y - y; |
822 | 0 | code = clist_rasterize_lines(dev, y, line_count, bdev, |
823 | 0 | &render_plane, &my); |
824 | 0 | if (code < 0) |
825 | 0 | break; |
826 | 0 | lines_rasterized = min(code, line_count); |
827 | 0 | band_rect.p.y = my; |
828 | 0 | band_rect.q.y = my + lines_rasterized; |
829 | 0 | code = dev_proc(bdev, get_bits_rectangle) |
830 | 0 | (bdev, &band_rect, &band_params); |
831 | 0 | if (code < 0) |
832 | 0 | break; |
833 | 0 | params->options = options = band_params.options; |
834 | 0 | if (lines_rasterized == line_count) |
835 | 0 | break; |
836 | 0 | } |
837 | 0 | cdev->buf_procs.destroy_buf_device(bdev); |
838 | 0 | } |
839 | 0 | return code; |
840 | 0 | } |
841 | | |
842 | | /* Copy scan lines to the client. This is where rendering gets done. */ |
843 | | /* Processes min(requested # lines, # lines available thru end of band) */ |
844 | | int /* returns -ve error code, or # scan lines copied */ |
845 | | clist_rasterize_lines(gx_device *dev, int y, int line_count, |
846 | | gx_device *bdev, const gx_render_plane_t *render_plane, |
847 | | int *pmy) |
848 | 160M | { |
849 | 160M | gx_device_clist * const cldev = (gx_device_clist *)dev; |
850 | 160M | gx_device_clist_reader * const crdev = &cldev->reader; |
851 | 160M | gx_device *target = crdev->target; |
852 | 160M | uint raster = clist_plane_raster(target, render_plane); |
853 | 160M | byte *mdata = crdev->data + crdev->page_info.tile_cache_size; |
854 | 160M | byte *mlines = (crdev->page_info.line_ptrs_offset == 0 ? NULL : mdata + crdev->page_info.line_ptrs_offset); |
855 | 160M | int plane_index = (render_plane ? render_plane->index : -1); |
856 | 160M | int code; |
857 | | |
858 | | /* Render a band if necessary, and copy it incrementally. */ |
859 | 160M | if (crdev->ymin < 0 || crdev->yplane.index != plane_index || |
860 | 160M | !(y >= crdev->ymin && y < crdev->ymax) |
861 | 160M | ) { |
862 | 2.96M | int band_height = crdev->page_info.band_params.BandHeight; |
863 | 2.96M | int band = y / band_height; |
864 | 2.96M | int band_begin_line = band * band_height; |
865 | 2.96M | int band_end_line = band_begin_line + band_height; |
866 | 2.96M | int band_num_lines; |
867 | 2.96M | gs_int_rect band_rect; |
868 | | |
869 | 2.96M | if (band_end_line > dev->height) |
870 | 68.0k | band_end_line = dev->height; |
871 | | /* Clip line_count to current band */ |
872 | 2.96M | if (line_count > band_end_line - y) |
873 | 0 | line_count = band_end_line - y; |
874 | 2.96M | band_num_lines = band_end_line - band_begin_line; |
875 | | |
876 | 2.96M | if (y < 0 || y > dev->height) |
877 | 0 | return_error(gs_error_rangecheck); |
878 | 2.96M | code = crdev->buf_procs.setup_buf_device |
879 | 2.96M | (bdev, mdata, raster, (byte **)mlines, 0, band_num_lines, band_num_lines); |
880 | 2.96M | band_rect.p.x = 0; |
881 | 2.96M | band_rect.p.y = band_begin_line; |
882 | 2.96M | band_rect.q.x = dev->width; |
883 | 2.96M | band_rect.q.y = band_end_line; |
884 | 2.96M | if (code >= 0) |
885 | 2.96M | code = clist_render_rectangle(cldev, &band_rect, bdev, render_plane, |
886 | 2.96M | true); |
887 | | /* Reset the band boundaries now, so that we don't get */ |
888 | | /* an infinite loop. */ |
889 | 2.96M | crdev->ymin = band_begin_line; |
890 | 2.96M | crdev->ymax = band_end_line; |
891 | 2.96M | crdev->offset_map = NULL; |
892 | 2.96M | if (code < 0) |
893 | 11 | return code; |
894 | 2.96M | } |
895 | | |
896 | 160M | if (line_count > crdev->ymax - y) |
897 | 0 | line_count = crdev->ymax - y; |
898 | 160M | code = crdev->buf_procs.setup_buf_device |
899 | 160M | (bdev, mdata, raster, (byte **)mlines, y - crdev->ymin, line_count, |
900 | 160M | crdev->ymax - crdev->ymin); |
901 | 160M | if (code < 0) |
902 | 0 | return code; |
903 | | |
904 | 160M | *pmy = 0; |
905 | 160M | return line_count; |
906 | 160M | } |
907 | | |
908 | | /* |
909 | | * Render a rectangle to a client-supplied device. There is no necessary |
910 | | * relationship between band boundaries and the region being rendered. |
911 | | */ |
912 | | int |
913 | | clist_render_rectangle(gx_device_clist *cldev, const gs_int_rect *prect, |
914 | | gx_device *bdev, |
915 | | const gx_render_plane_t *render_plane, bool clear) |
916 | 2.96M | { |
917 | 2.96M | gx_device_clist_reader * const crdev = &cldev->reader; |
918 | 2.96M | const gx_placed_page *ppages; |
919 | 2.96M | int num_pages = crdev->num_pages; |
920 | 2.96M | int band_height = crdev->page_info.band_params.BandHeight; |
921 | 2.96M | int band_first = prect->p.y / band_height; |
922 | 2.96M | int band_last = (prect->q.y - 1) / band_height; |
923 | 2.96M | gx_band_page_info_t *pinfo; |
924 | 2.96M | gx_band_page_info_t page_info; |
925 | 2.96M | int code = 0; |
926 | 2.96M | int i; |
927 | 2.96M | bool save_pageneutralcolor; |
928 | | |
929 | 2.96M | if (render_plane) |
930 | 2.96M | crdev->yplane = *render_plane; |
931 | 0 | else |
932 | 0 | crdev->yplane.index = -1; |
933 | 2.96M | if_debug2m('l', bdev->memory, "[l]rendering bands (%d,%d)\n", band_first, band_last); |
934 | | |
935 | 2.96M | ppages = crdev->pages; |
936 | | |
937 | | /* Before playing back the clist, make sure that the gray detection is disabled */ |
938 | | /* so we don't slow down the rendering (primarily high level images). */ |
939 | 2.96M | save_pageneutralcolor = crdev->icc_struct->pageneutralcolor; |
940 | 2.96M | crdev->icc_struct->pageneutralcolor = false; |
941 | | |
942 | 5.93M | for (i = 0; i < num_pages && code >= 0; ++i) { |
943 | 2.96M | bool pdf14_needed = false; |
944 | 2.96M | int band; |
945 | | |
946 | 2.96M | if (ppages == NULL) { |
947 | | /* |
948 | | * If we aren't rendering saved pages, do the current one. |
949 | | * Note that this is the only case in which we may encounter |
950 | | * a gx_saved_page with non-zero cfile or bfile. |
951 | | */ |
952 | 2.96M | bdev->band_offset_x = 0; |
953 | 2.96M | bdev->band_offset_y = band_first * (long)band_height; |
954 | 2.96M | pinfo = &(crdev->page_info); |
955 | 2.96M | } else { |
956 | 0 | const gx_placed_page *ppage = &ppages[i]; |
957 | | |
958 | | /* Store the page information. */ |
959 | 0 | page_info.cfile = page_info.bfile = NULL; |
960 | 0 | memcpy(page_info.cfname, ppage->page->cfname, sizeof(page_info.cfname)); |
961 | 0 | memcpy(page_info.bfname, ppage->page->bfname, sizeof(page_info.bfname)); |
962 | 0 | page_info.io_procs = ppage->page->io_procs; |
963 | 0 | page_info.tile_cache_size = ppage->page->tile_cache_size; |
964 | 0 | page_info.line_ptrs_offset = ppage->page->line_ptrs_offset; |
965 | 0 | page_info.bfile_end_pos = ppage->page->bfile_end_pos; |
966 | 0 | page_info.band_params = ppage->page->band_params; |
967 | 0 | pinfo = &page_info; |
968 | | |
969 | | /* |
970 | | * Set the band_offset_? values in case the buffer device |
971 | | * needs this. Example, a device may need to adjust the |
972 | | * phase of the dithering based on the page position, NOT |
973 | | * the position within the band buffer to avoid band stitch |
974 | | * lines in the dither pattern. The old wtsimdi device did this |
975 | | * |
976 | | * The band_offset_x is not important for placed pages that |
977 | | * are nested on a 'master' page (imposition) since each |
978 | | * page expects to be dithered independently, but setting |
979 | | * this allows pages to be contiguous without a dithering |
980 | | * shift. |
981 | | * |
982 | | * The following sets the band_offset_? relative to the |
983 | | * master page. |
984 | | */ |
985 | 0 | bdev->band_offset_x = ppage->offset.x; |
986 | 0 | bdev->band_offset_y = ppage->offset.y + (band_first * band_height); |
987 | 0 | } |
988 | | /* if any of the requested bands need transparency, use it for all of them */ |
989 | | /* The pdf14_ok_to_optimize checks if the target device (bdev) is compatible */ |
990 | | /* with the pdf14 compositor info that was written to the clist: colorspace, */ |
991 | | /* colorspace, etc. */ |
992 | 2.96M | pdf14_needed = !pdf14_ok_to_optimize(bdev); |
993 | 4.64M | for (band=band_first; !pdf14_needed && band <= band_last; band++) |
994 | 1.67M | pdf14_needed |= (crdev->color_usage_array[band].trans_bbox.p.y <= |
995 | 1.67M | crdev->color_usage_array[band].trans_bbox.q.y) ? true : false; |
996 | | |
997 | 2.96M | code = clist_playback_file_bands(pdf14_needed ? |
998 | 2.14M | playback_action_render : playback_action_render_no_pdf14, |
999 | 2.96M | crdev, pinfo, |
1000 | 2.96M | bdev, band_first, band_last, |
1001 | 2.96M | prect->p.x - bdev->band_offset_x, |
1002 | 2.96M | prect->p.y); |
1003 | 2.96M | } |
1004 | 2.96M | crdev->icc_struct->pageneutralcolor = save_pageneutralcolor; /* restore it */ |
1005 | 2.96M | return code; |
1006 | 2.96M | } |
1007 | | |
1008 | | /* Playback the band file, taking the indicated action w/ its contents. */ |
1009 | | int |
1010 | | clist_playback_file_bands(clist_playback_action action, |
1011 | | gx_device_clist_reader *crdev, |
1012 | | gx_band_page_info_t *page_info, gx_device *target, |
1013 | | int band_first, int band_last, int x0, int y0) |
1014 | 3.00M | { |
1015 | 3.00M | int code = 0; |
1016 | 3.00M | bool opened_bfile = false; |
1017 | 3.00M | bool opened_cfile = false; |
1018 | | |
1019 | | /* We have to pick some allocator for rendering.... */ |
1020 | 3.00M | gs_memory_t *mem =crdev->memory; |
1021 | | |
1022 | 3.00M | stream_band_read_state rs; |
1023 | | |
1024 | | /* setup stream */ |
1025 | 3.00M | s_init_state((stream_state *)&rs, &s_band_read_template, |
1026 | 3.00M | (gs_memory_t *)0); /* Not mem, as we don't want to free rs */ |
1027 | 3.00M | rs.band_first = band_first; |
1028 | 3.00M | rs.band_last = band_last; |
1029 | 3.00M | rs.page_info = *page_info; |
1030 | 3.00M | rs.local_memory = mem; |
1031 | | |
1032 | | /* If this is a saved page, open the files. */ |
1033 | 3.00M | if (rs.page_info.cfile == 0) { |
1034 | 0 | code = crdev->page_info.io_procs->fopen(rs.page_info.cfname, |
1035 | 0 | gp_fmode_rb, &rs.page_info.cfile, crdev->bandlist_memory, |
1036 | 0 | crdev->bandlist_memory, true); |
1037 | 0 | opened_cfile = (code >= 0); |
1038 | 0 | } |
1039 | 3.00M | if (rs.page_info.bfile == 0 && code >= 0) { |
1040 | 0 | code = crdev->page_info.io_procs->fopen(rs.page_info.bfname, |
1041 | 0 | gp_fmode_rb, &rs.page_info.bfile, crdev->bandlist_memory, |
1042 | 0 | crdev->bandlist_memory, false); |
1043 | 0 | opened_bfile = (code >= 0); |
1044 | 0 | } |
1045 | 3.00M | if (rs.page_info.cfile != 0 && rs.page_info.bfile != 0) { |
1046 | 3.00M | stream s; |
1047 | 3.00M | byte sbuf[cbuf_size]; |
1048 | 3.00M | static const stream_procs no_procs = { |
1049 | 3.00M | s_std_noavailable, s_std_noseek, s_std_read_reset, |
1050 | 3.00M | s_std_read_flush, s_std_close, s_band_read_process |
1051 | 3.00M | }; |
1052 | | |
1053 | 3.00M | s_band_read_init((stream_state *)&rs); |
1054 | | # ifdef DEBUG |
1055 | | s_band_read_init_offset_map(crdev, (stream_state *)&rs); |
1056 | | # endif |
1057 | | /* The stream doesn't need a memory, but we'll need to access s.memory->gs_lib_ctx. */ |
1058 | 3.00M | s_init(&s, mem); |
1059 | 3.00M | s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read); |
1060 | 3.00M | s.foreign = 1; |
1061 | 3.00M | s.state = (stream_state *)&rs; |
1062 | | |
1063 | 3.00M | code = clist_playback_band(action, crdev, &s, target, x0, y0, mem); |
1064 | | # ifdef DEBUG |
1065 | | s_band_read_dnit_offset_map(crdev, (stream_state *)&rs); |
1066 | | # endif |
1067 | 3.00M | } |
1068 | | |
1069 | | /* Close the files if we just opened them. */ |
1070 | 3.00M | if (opened_bfile && rs.page_info.bfile != 0) |
1071 | 0 | crdev->page_info.io_procs->fclose(rs.page_info.bfile, rs.page_info.bfname, false); |
1072 | 3.00M | if (opened_cfile && rs.page_info.cfile != 0) |
1073 | 0 | crdev->page_info.io_procs->fclose(rs.page_info.cfile, rs.page_info.cfname, false); |
1074 | | |
1075 | 3.00M | return code; |
1076 | 3.00M | } |