/src/ghostpdl/base/gxclread.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, 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 | 56.5k | { |
87 | 56.5k | stream_band_read_state *const ss = (stream_band_read_state *) st; |
88 | 56.5k | const clist_io_procs_t *io_procs = ss->page_info.io_procs; |
89 | | |
90 | 56.5k | ss->left = 0; |
91 | 56.5k | ss->b_this.band_min = 0; |
92 | 56.5k | ss->b_this.band_max = 0; |
93 | 56.5k | ss->b_this.pos = 0; |
94 | 56.5k | return io_procs->rewind(ss->page_bfile, false, ss->page_bfname); |
95 | 56.5k | } |
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 | 535k | { |
142 | 535k | stream_band_read_state *const ss = (stream_band_read_state *) st; |
143 | 535k | register byte *q = pw->ptr; |
144 | 535k | byte *wlimit = pw->limit; |
145 | 535k | clist_file_ptr cfile = ss->page_cfile; |
146 | 535k | clist_file_ptr bfile = ss->page_bfile; |
147 | 535k | uint left = ss->left; |
148 | 535k | int status = 1; |
149 | 535k | uint count; |
150 | 535k | const clist_io_procs_t *io_procs = ss->page_info.io_procs; |
151 | 535k | 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 | 7.99M | while ((count = wlimit - q) != 0) { |
156 | 7.51M | int bmin, bmax; |
157 | | /* If there is more data to be read in the current command, then pull that in. */ |
158 | 7.51M | if (left) { |
159 | 3.96M | if (count > left) |
160 | 3.48M | 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 | 3.96M | io_procs->fread_chars(q + 1, count, cfile); |
186 | 3.96M | if (io_procs->ferror_code(cfile) < 0) { |
187 | 0 | status = ERRC; |
188 | 0 | break; |
189 | 0 | } |
190 | 3.96M | q += count; |
191 | 3.96M | left -= count; |
192 | 3.96M | process_interrupts(ss->local_memory); |
193 | 3.96M | continue; |
194 | 3.96M | } |
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 | 85.6M | do { |
198 | 85.6M | int nread; |
199 | | /* If we hit eof, end! */ |
200 | | /* Could this test be moved into the nread < sizeof() test below? */ |
201 | 85.6M | if (ss->b_this.band_min == cmd_band_end && |
202 | 85.6M | io_procs->ftell(bfile) == ss->page_bfile_end_pos) { |
203 | 56.1k | pw->ptr = q; |
204 | 56.1k | ss->left = left; |
205 | 56.1k | return EOFC; |
206 | 56.1k | } |
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 | 85.5M | bmin = ss->b_this.band_min; |
214 | 85.5M | bmax = ss->b_this.band_max; |
215 | 85.5M | pos = ss->b_this.pos; /* Record where our data starts! */ |
216 | 85.5M | nread = io_procs->fread_chars(&ss->b_this, sizeof(ss->b_this), bfile); |
217 | 85.5M | 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 | 85.5M | } 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 | 3.49M | io_procs->fseek(cfile, pos, SEEK_SET, ss->page_cfname); |
224 | 3.49M | 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 | 3.49M | if_debug5m('l', ss->local_memory, |
237 | 3.49M | "[l]reading for bands (%d,%d) at bfile %"PRId64", cfile %"PRId64", length %u\n", |
238 | 3.49M | bmin, bmax, |
239 | 3.49M | (io_procs->ftell(bfile) - sizeof(ss->b_this)), (int64_t)pos, left); |
240 | 3.49M | } |
241 | 479k | pw->ptr = q; |
242 | 479k | ss->left = left; |
243 | 479k | return status; |
244 | 535k | } |
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 | | return ss->offset_map[i].file_offset + (uint)(buffer_offset - offset0); |
322 | | } |
323 | | |
324 | | void |
325 | | top_up_offset_map(stream_state * st, const byte *buf, const byte *ptr, const byte *end) |
326 | | { |
327 | | /* NOTE: The clist data are buffered in the clist reader buffer and in the |
328 | | internal buffer of the clist stream. Since the 1st buffer is not accessible |
329 | | from s_band_read_process, offset_map corresponds the union of the 2 buffers. |
330 | | */ |
331 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
332 | | uint buffer_offset, offset0, consumed; |
333 | | int i; |
334 | | |
335 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
336 | | if (ptr < buf || end < ptr || end < buf || end > buf + cbuf_size) |
337 | | { |
338 | | dmlprintf3(ss->local_memory, "Invalid pointers for top_up_offset_map: buf=%p ptr=%p end=%p\n", buf, ptr, end); |
339 | | } |
340 | | #endif |
341 | | |
342 | | if (!gs_debug_c('L')) |
343 | | return; |
344 | | if (ss->skip_first) { |
345 | | /* Work around the trick with initializing the buffer pointer with the buffer end. */ |
346 | | ss->skip_first = false; |
347 | | return; |
348 | | } |
349 | | if (ptr == buf) |
350 | | return; |
351 | | |
352 | | /* We know that buf <= ptr <= end <= buf+4096, so uint is quite enough! */ |
353 | | buffer_offset = ptr - buf; |
354 | | i = buffer_segment_index(ss, buffer_offset, &offset0); |
355 | | |
356 | | consumed = buffer_offset - offset0; |
357 | | #ifdef EXTRA_OFFSET_MAP_DEBUGGING |
358 | | dmlprintf3(ss->local_memory, "offset_map: dump %d entries + %d bytes + %d skipped bytes\n", i, consumed, ss->bytes_skipped); |
359 | | if (ss->offset_map[i].buffered < consumed) { |
360 | | dmlprintf2(ss->local_memory, "Invalid update to buffered. B %d %d\n", ss->offset_map[i].buffered, consumed); |
361 | | exit(1); |
362 | | } |
363 | | #endif |
364 | | ss->offset_map[i].buffered -= consumed; |
365 | | ss->offset_map[i].file_offset += consumed; |
366 | | ss->bytes_skipped = 0; |
367 | | if (i) { |
368 | | memmove(ss->offset_map, ss->offset_map + i, |
369 | | (ss->offset_map_length - i) * sizeof(*ss->offset_map)); |
370 | | ss->offset_map_length -= i; |
371 | | } |
372 | | } |
373 | | |
374 | | /* This function is called when data is copied from the stream out into a separate |
375 | | * buffer without going through the usual clist buffers. Essentially data for the |
376 | | * id we are reading at buffer_offset within the buffer is skipped. */ |
377 | | void adjust_offset_map_for_skipped_data(stream_state *st, uint buffer_offset, uint skipped) |
378 | | { |
379 | | uint offset0; |
380 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
381 | | int i; |
382 | | |
383 | | if (!gs_debug_c('L')) |
384 | | return; |
385 | | |
386 | | i = buffer_segment_index(ss, buffer_offset, &offset0); |
387 | | |
388 | | ss->offset_map[i].buffered -= skipped; |
389 | | ss->offset_map[i].file_offset += skipped; |
390 | | } |
391 | | |
392 | | void |
393 | | offset_map_next_data_out_of_band(stream_state *st) |
394 | | { |
395 | | stream_band_read_state *const ss = (stream_band_read_state *) st; |
396 | | |
397 | | if (!gs_debug_c('L')) |
398 | | return; |
399 | | |
400 | | ss->skip_next = 1; |
401 | | } |
402 | | #endif /* DEBUG */ |
403 | | |
404 | | /* ------ Reading/rendering ------ */ |
405 | | |
406 | | /* Calculate the raster for a chunky or planar device. */ |
407 | | static int |
408 | | clist_plane_raster(const gx_device *dev, const gx_render_plane_t *render_plane) |
409 | 2.57M | { |
410 | 2.57M | return gx_device_raster_plane(dev, render_plane); |
411 | 2.57M | } |
412 | | |
413 | | /* Select full-pixel rendering if required for RasterOp. */ |
414 | | void |
415 | | clist_select_render_plane(gx_device *dev, int y, int height, |
416 | | gx_render_plane_t *render_plane, int index) |
417 | 2.57M | { |
418 | 2.57M | if (index >= 0) { |
419 | 0 | gx_color_usage_t color_usage; |
420 | 0 | int ignore_start; |
421 | |
|
422 | 0 | gdev_prn_color_usage(dev, y, height, &color_usage, &ignore_start); |
423 | 0 | if (color_usage.slow_rop) |
424 | 0 | index = -1; |
425 | 0 | } |
426 | 2.57M | if (index < 0) |
427 | 2.57M | render_plane->index = index; |
428 | 0 | else |
429 | 0 | gx_render_plane_init(render_plane, dev, index); |
430 | 2.57M | } |
431 | | |
432 | | /* |
433 | | * Do device setup from params stored in command list. This is only for |
434 | | * async rendering & assumes that the first command in every command list |
435 | | * is a put_params command which sets all space-related parameters to the |
436 | | * value they will have for the duration of that command list. |
437 | | */ |
438 | | int |
439 | | clist_setup_params(gx_device *dev) |
440 | 0 | { |
441 | 0 | gx_device_clist *cldev = (gx_device_clist *)dev; |
442 | 0 | gx_device_clist_reader * const crdev = &cldev->reader; |
443 | 0 | int code = clist_render_init(cldev); |
444 | |
|
445 | 0 | if (code < 0) |
446 | 0 | return code; |
447 | | |
448 | 0 | code = clist_playback_file_bands(playback_action_setup, |
449 | 0 | crdev, &crdev->page_info, 0, 0, 0, 0, 0); |
450 | | |
451 | | /* put_params may have reinitialized device into a writer */ |
452 | 0 | clist_render_init(cldev); |
453 | |
|
454 | 0 | return code; |
455 | 0 | } |
456 | | |
457 | | int |
458 | | clist_close_writer_and_init_reader(gx_device_clist *cldev) |
459 | 2.58M | { |
460 | 2.58M | gx_device_clist_reader * const crdev = &cldev->reader; |
461 | 2.58M | gs_memory_t *base_mem = crdev->memory->thread_safe_memory; |
462 | 2.58M | gs_memory_status_t mem_status; |
463 | 2.58M | int code = 0; |
464 | | |
465 | | /* Initialize for rendering if we haven't done so yet. */ |
466 | 2.58M | if (crdev->ymin < 0) { |
467 | 6.82k | code = clist_end_page(&cldev->writer); |
468 | 6.82k | if (code < 0) |
469 | 3 | return code; |
470 | 6.82k | code = clist_render_init(cldev); |
471 | 6.82k | if (code < 0) |
472 | 0 | return code; |
473 | | /* allocate and load the color_usage_array */ |
474 | 6.82k | code = clist_read_color_usage_array(crdev); |
475 | 6.82k | if (code < 0) |
476 | 0 | return code; |
477 | | /* Check for and get ICC profile table */ |
478 | 6.82k | code = clist_read_icctable(crdev); |
479 | 6.82k | if (code < 0) |
480 | 0 | return code; |
481 | | /* Allocate the icc cache for the clist reader */ |
482 | | /* Since we may be rendering in multiple threads, make sure the memory */ |
483 | | /* is thread safe by using a known thread_safe memory allocator */ |
484 | 6.82k | gs_memory_status(base_mem, &mem_status); |
485 | 6.82k | if (mem_status.is_thread_safe == false) { |
486 | 0 | return_error(gs_error_VMerror); |
487 | 0 | } |
488 | | |
489 | 6.82k | if (crdev->icc_cache_cl == NULL) { |
490 | 3.03k | code = (crdev->icc_cache_cl = gsicc_cache_new(base_mem)) == NULL ? gs_error_VMerror : code; |
491 | 3.03k | } |
492 | 6.82k | } |
493 | | |
494 | 2.58M | check_device_compatible_encoding((gx_device *)cldev); |
495 | | |
496 | 2.58M | return code; |
497 | 2.58M | } |
498 | | |
499 | | /* Used to find the command block information in the bfile |
500 | | that is related to extra information stored in a psuedo band. |
501 | | Currently application of this is storage of the ICC profile |
502 | | table, the per-band color_usage array, and the spot equivalent |
503 | | colors when doing overprint simulation. We may eventually |
504 | | use this for storing other information like compressed images. */ |
505 | | |
506 | | static int |
507 | | clist_find_pseudoband(gx_device_clist_reader *crdev, int band, cmd_block *cb) |
508 | 13.6k | { |
509 | | |
510 | 13.6k | gx_band_page_info_t *page_info = &(crdev->page_info); |
511 | 13.6k | clist_file_ptr bfile = page_info->bfile; |
512 | 13.6k | int64_t save_pos = page_info->bfile_end_pos; |
513 | 13.6k | int64_t start_pos; |
514 | 13.6k | int code; |
515 | | |
516 | 13.6k | if (bfile == NULL) { |
517 | | /* files haven't been opened yet. Do it now */ |
518 | 0 | char fmode[4]; |
519 | |
|
520 | 0 | strcpy(fmode, "r"); |
521 | 0 | strncat(fmode, gp_fmode_binary_suffix, 1); |
522 | 0 | if ((code=page_info->io_procs->fopen(page_info->cfname, fmode, |
523 | 0 | &page_info->cfile, |
524 | 0 | crdev->memory, crdev->memory, true)) < 0 || |
525 | 0 | (code=page_info->io_procs->fopen(page_info->bfname, fmode, |
526 | 0 | &page_info->bfile, |
527 | 0 | crdev->memory, crdev->memory, false)) < 0) { |
528 | 0 | return code; |
529 | 0 | } |
530 | 0 | bfile = page_info->bfile; |
531 | 0 | } |
532 | | /* Go to the start of the last command block */ |
533 | 13.6k | start_pos = page_info->bfile_end_pos - sizeof(cmd_block); |
534 | 13.6k | page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname); |
535 | 227k | while( 1 ) { |
536 | 227k | int read = page_info->io_procs->fread_chars(cb, sizeof(cmd_block), bfile); |
537 | | |
538 | 227k | if (read < sizeof(cmd_block)) |
539 | 0 | return -1; |
540 | 227k | if (cb->band_max == band && cb->band_min == band) { |
541 | 7.73k | page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname); |
542 | 7.73k | return(0); /* Found it */ |
543 | 7.73k | } |
544 | 219k | start_pos -= sizeof(cmd_block); |
545 | 219k | if (start_pos < 0) { |
546 | 5.91k | page_info->io_procs->fseek(bfile, save_pos, SEEK_SET, page_info->bfname); |
547 | 5.91k | return(-1); /* Did not find it before getting into other stuff in normal bands */ |
548 | 5.91k | } |
549 | 213k | page_info->io_procs->fseek(bfile, start_pos, SEEK_SET, page_info->bfname); |
550 | 213k | } |
551 | 13.6k | } |
552 | | |
553 | | /* A procedure to read a chunk of data from the cfile at a particular location into buff */ |
554 | | int |
555 | | clist_read_chunk(gx_device_clist_reader *crdev, int64_t position, int size, unsigned char *buf) |
556 | 69.4k | { |
557 | 69.4k | clist_file_ptr cfile = crdev->page_info.cfile; |
558 | 69.4k | int64_t save_pos; |
559 | | |
560 | | /* Save our current location */ |
561 | 69.4k | save_pos = crdev->page_info.io_procs->ftell(cfile); |
562 | | /* Go to our new position */ |
563 | 69.4k | crdev->page_info.io_procs->fseek(cfile, position, SEEK_SET, crdev->page_info.cfname); |
564 | | /* Get the data */ |
565 | 69.4k | crdev->page_info.io_procs->fread_chars(buf, size, cfile); |
566 | | /* Restore our position */ |
567 | 69.4k | crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname); |
568 | 69.4k | return 0; |
569 | 69.4k | } |
570 | | |
571 | | /* read the color_usage_array back from the pseudo band */ |
572 | | int |
573 | | clist_read_color_usage_array(gx_device_clist_reader *crdev) |
574 | 6.82k | { |
575 | 6.82k | int code, size_data = crdev->nbands * sizeof(gx_color_usage_t ); |
576 | 6.82k | cmd_block cb; |
577 | | |
578 | 6.82k | if (crdev->color_usage_array != NULL) |
579 | 0 | gs_free_object(crdev->memory, crdev->color_usage_array, |
580 | 6.82k | "clist reader color_usage_array"); |
581 | 6.82k | crdev->color_usage_array = (gx_color_usage_t *)gs_alloc_bytes(crdev->memory, size_data, |
582 | 6.82k | "clist reader color_usage_array"); |
583 | 6.82k | if (crdev->color_usage_array == NULL) |
584 | 0 | return_error(gs_error_VMerror); |
585 | | |
586 | 6.82k | code = clist_find_pseudoband(crdev, crdev->nbands + COLOR_USAGE_OFFSET - 1, &cb); |
587 | 6.82k | if (code < 0) |
588 | 0 | return code; |
589 | | |
590 | 6.82k | code = clist_read_chunk(crdev, cb.pos, size_data, (unsigned char *)crdev->color_usage_array); |
591 | 6.82k | return code; |
592 | 6.82k | } |
593 | | |
594 | | /* read the cmyk equivalent spot colors */ |
595 | | int |
596 | | clist_read_op_equiv_cmyk_colors(gx_device_clist_reader *crdev, |
597 | | equivalent_cmyk_color_params *op_equiv_cmyk_colors) |
598 | 0 | { |
599 | 0 | int code; |
600 | 0 | cmd_block cb; |
601 | |
|
602 | 0 | code = clist_find_pseudoband(crdev, crdev->nbands + SPOT_EQUIV_COLORS - 1, &cb); |
603 | 0 | if (code < 0) |
604 | 0 | return code; |
605 | | |
606 | 0 | code = clist_read_chunk(crdev, cb.pos, sizeof(equivalent_cmyk_color_params), |
607 | 0 | (unsigned char *)op_equiv_cmyk_colors); |
608 | 0 | return code; |
609 | 0 | } |
610 | | |
611 | | /* Unserialize the icc table information stored in the cfile and |
612 | | place it in the reader device */ |
613 | | static int |
614 | | clist_unserialize_icctable(gx_device_clist_reader *crdev, cmd_block *cb) |
615 | 911 | { |
616 | 911 | clist_file_ptr cfile = crdev->page_info.cfile; |
617 | 911 | clist_icctable_t *icc_table = crdev->icc_table; |
618 | 911 | int64_t save_pos; |
619 | 911 | int number_entries, size_data; |
620 | 911 | unsigned char *buf, *buf_start; |
621 | 911 | clist_icctable_entry_t *curr_entry; |
622 | 911 | int k; |
623 | 911 | gs_memory_t *stable_mem = crdev->memory->stable_memory; |
624 | | |
625 | 911 | if ( icc_table != NULL ) |
626 | 0 | return(0); |
627 | 911 | save_pos = crdev->page_info.io_procs->ftell(cfile); |
628 | 911 | crdev->page_info.io_procs->fseek(cfile, cb->pos, SEEK_SET, crdev->page_info.cfname); |
629 | | /* First four bytes tell us the number of entries. */ |
630 | 911 | crdev->page_info.io_procs->fread_chars(&number_entries, sizeof(number_entries), cfile); |
631 | | /* Allocate the space */ |
632 | 911 | size_data = number_entries*sizeof(clist_icc_serial_entry_t); |
633 | 911 | buf = gs_alloc_bytes(crdev->memory, size_data, "clist_read_icctable"); |
634 | 911 | buf_start = buf; |
635 | 911 | if (buf == NULL) |
636 | 0 | return gs_rethrow(-1, "insufficient memory for icc table buffer reader"); |
637 | | /* Get the data */ |
638 | 911 | clist_read_chunk(crdev, cb->pos + 4, size_data, buf); |
639 | 911 | icc_table = gs_alloc_struct(stable_mem, clist_icctable_t, |
640 | 911 | &st_clist_icctable, "clist_read_icctable"); |
641 | 911 | if (icc_table == NULL) { |
642 | 0 | gs_free_object(stable_mem, buf_start, "clist_read_icctable"); |
643 | 0 | return gs_rethrow(-1, "insufficient memory for icc table buffer reader"); |
644 | 0 | } |
645 | 911 | icc_table->memory = stable_mem; |
646 | 911 | icc_table->head = NULL; |
647 | 911 | icc_table->final = NULL; |
648 | | /* Allocate and fill each entry */ |
649 | 911 | icc_table->tablesize = number_entries; |
650 | 911 | crdev->icc_table = icc_table; |
651 | 2.05k | for (k = 0; k < number_entries; k++) { |
652 | 1.14k | curr_entry = gs_alloc_struct(stable_mem, clist_icctable_entry_t, |
653 | 1.14k | &st_clist_icctable_entry, "clist_read_icctable"); |
654 | 1.14k | if (curr_entry == NULL) { |
655 | 0 | gs_free_object(stable_mem, buf_start, "clist_read_icctable"); |
656 | 0 | return gs_rethrow(-1, "insufficient memory for icc table entry"); |
657 | 0 | } |
658 | 1.14k | memcpy(&(curr_entry->serial_data), buf, sizeof(clist_icc_serial_entry_t)); |
659 | 1.14k | buf += sizeof(clist_icc_serial_entry_t); |
660 | 1.14k | curr_entry->icc_profile = NULL; |
661 | 1.14k | if ( icc_table->head == NULL ) { |
662 | 911 | icc_table->head = curr_entry; |
663 | 911 | icc_table->final = curr_entry; |
664 | 911 | } else { |
665 | 229 | icc_table->final->next = curr_entry; |
666 | 229 | icc_table->final = curr_entry; |
667 | 229 | } |
668 | 1.14k | curr_entry->next = NULL; |
669 | 1.14k | } |
670 | 911 | gs_free_object(crdev->memory, buf_start, "clist_read_icctable"); |
671 | 911 | crdev->page_info.io_procs->fseek(cfile, save_pos, SEEK_SET, crdev->page_info.cfname); |
672 | 911 | return 0; |
673 | 911 | } |
674 | | |
675 | | /* Get the ICC profile table information from the clist */ |
676 | | int |
677 | | clist_read_icctable(gx_device_clist_reader *crdev) |
678 | 6.82k | { |
679 | | /* Look for the command block of the ICC Profile. */ |
680 | 6.82k | cmd_block cb; |
681 | 6.82k | int code; |
682 | | |
683 | | /* First get the command block which will tell us where the |
684 | | information is stored in the cfile */ |
685 | 6.82k | code = clist_find_pseudoband(crdev, crdev->nbands + ICC_TABLE_OFFSET - 1, &cb); |
686 | 6.82k | if (code < 0) |
687 | 5.91k | return(0); /* No ICC information */ |
688 | | /* Unserialize the icc_table from the cfile */ |
689 | 911 | code = clist_unserialize_icctable(crdev, &cb); |
690 | 911 | return(code); |
691 | 6.82k | } |
692 | | |
693 | | /* Initialize for reading. */ |
694 | | int |
695 | | clist_render_init(gx_device_clist *dev) |
696 | 6.82k | { |
697 | 6.82k | gx_device_clist_reader * const crdev = &dev->reader; |
698 | | |
699 | 6.82k | crdev->ymin = crdev->ymax = 0; |
700 | 6.82k | crdev->yplane.index = -1; |
701 | | /* For normal rasterizing, pages and num_pages is 1. */ |
702 | 6.82k | crdev->pages = 0; |
703 | 6.82k | crdev->num_pages = 1; /* always at least one page */ |
704 | 6.82k | crdev->offset_map = NULL; |
705 | 6.82k | crdev->icc_table = NULL; |
706 | 6.82k | crdev->color_usage_array = NULL; |
707 | 6.82k | crdev->render_threads = NULL; |
708 | | |
709 | 6.82k | return 0; |
710 | 6.82k | } |
711 | | |
712 | | /* Copy a rasterized rectangle to the client, rasterizing if needed. */ |
713 | | int |
714 | | clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect, |
715 | | gs_get_bits_params_t *params) |
716 | 2.57M | { |
717 | 2.57M | gx_device_clist *cldev = (gx_device_clist *)dev; |
718 | 2.57M | gx_device_clist_reader *crdev = &cldev->reader; |
719 | 2.57M | gx_device_clist_common *cdev = (gx_device_clist_common *)dev; |
720 | 2.57M | gs_get_bits_options_t options = params->options; |
721 | 2.57M | int y = prect->p.y; |
722 | 2.57M | int end_y = prect->q.y; |
723 | 2.57M | int line_count = end_y - y; |
724 | 2.57M | gs_int_rect band_rect; |
725 | 2.57M | int lines_rasterized; |
726 | 2.57M | gx_device *bdev; |
727 | 2.57M | uint num_planes = |
728 | 2.57M | (options & GB_PACKING_CHUNKY ? 1 : |
729 | 2.57M | options & GB_PACKING_PLANAR ? dev->color_info.num_components : |
730 | 0 | options & GB_PACKING_BIT_PLANAR ? dev->color_info.depth : |
731 | 0 | 0 /****** NOT POSSIBLE ******/); |
732 | 2.57M | gx_render_plane_t render_plane; |
733 | 2.57M | int plane_index; |
734 | 2.57M | int my; |
735 | 2.57M | int code; |
736 | | |
737 | 2.57M | if (prect->p.x < 0 || prect->q.x > dev->width || |
738 | 2.57M | y < 0 || end_y > dev->height |
739 | 2.57M | ) |
740 | 0 | return_error(gs_error_rangecheck); |
741 | 2.57M | if (line_count <= 0 || prect->p.x >= prect->q.x) |
742 | 0 | return 0; |
743 | | |
744 | | /* |
745 | | * Calculate the render_plane from the params. There are two cases: |
746 | | * full pixels, or a single plane. |
747 | | */ |
748 | 2.57M | plane_index = -1; |
749 | 2.57M | if (options & GB_SELECT_PLANES) { |
750 | | /* Look for the one selected plane. */ |
751 | 0 | int i; |
752 | |
|
753 | 0 | for (i = 0; i < num_planes; ++i) |
754 | 0 | if (params->data[i]) { |
755 | 0 | if (plane_index >= 0) /* >1 plane requested */ |
756 | 0 | return gx_default_get_bits_rectangle(dev, prect, params); |
757 | 0 | plane_index = i; |
758 | 0 | } |
759 | 0 | } |
760 | | |
761 | 2.57M | if (0 > (code = clist_close_writer_and_init_reader(cldev))) |
762 | 3 | return code; |
763 | | |
764 | 2.57M | clist_select_render_plane(dev, y, line_count, &render_plane, plane_index); |
765 | 2.57M | code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, |
766 | 2.57M | &bdev, cdev->target, y, &render_plane, |
767 | 2.57M | dev->memory, |
768 | 2.57M | &(crdev->color_usage_array[y/crdev->page_band_height])); |
769 | 2.57M | if (code < 0) |
770 | 0 | return code; |
771 | 2.57M | code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my); |
772 | 2.57M | if (code >= 0) { |
773 | 2.57M | lines_rasterized = min(code, line_count); |
774 | | /* Return as much of the rectangle as falls within the rasterized lines. */ |
775 | 2.57M | band_rect = *prect; |
776 | 2.57M | band_rect.p.y = my; |
777 | 2.57M | band_rect.q.y = my + lines_rasterized; |
778 | 2.57M | code = dev_proc(bdev, get_bits_rectangle) |
779 | 2.57M | (bdev, &band_rect, params); |
780 | 2.57M | } |
781 | 2.57M | cdev->buf_procs.destroy_buf_device(bdev); |
782 | 2.57M | if (code < 0 || lines_rasterized == line_count) |
783 | 2.57M | return code; |
784 | | /* |
785 | | * We'll have to return the rectangle in pieces. Force GB_RETURN_COPY |
786 | | * rather than GB_RETURN_POINTER, and require all subsequent pieces to |
787 | | * use the same values as the first piece for all of the other format |
788 | | * options. If copying isn't allowed, or if there are any unread |
789 | | * rectangles, punt. |
790 | | */ |
791 | 0 | if (!(options & GB_RETURN_COPY) || code > 0) |
792 | 0 | return gx_default_get_bits_rectangle(dev, prect, params); |
793 | 0 | options = params->options; |
794 | 0 | if (!(options & GB_RETURN_COPY)) { |
795 | | /* Redo the first piece with copying. */ |
796 | 0 | params->options = options = |
797 | 0 | (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY; |
798 | 0 | lines_rasterized = 0; |
799 | 0 | } |
800 | 0 | { |
801 | 0 | gs_get_bits_params_t band_params; |
802 | 0 | uint raster = gx_device_raster(bdev, true); |
803 | |
|
804 | 0 | code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, |
805 | 0 | &bdev, cdev->target, y, &render_plane, |
806 | 0 | dev->memory, |
807 | 0 | &(crdev->color_usage_array[y/crdev->page_band_height])); |
808 | 0 | if (code < 0) |
809 | 0 | return code; |
810 | 0 | band_params = *params; |
811 | 0 | while ((y += lines_rasterized) < end_y) { |
812 | 0 | int i; |
813 | | |
814 | | /* Increment data pointers by lines_rasterized. */ |
815 | 0 | for (i = 0; i < num_planes; ++i) |
816 | 0 | if (band_params.data[i]) |
817 | 0 | band_params.data[i] += raster * lines_rasterized; |
818 | 0 | line_count = end_y - y; |
819 | 0 | code = clist_rasterize_lines(dev, y, line_count, bdev, |
820 | 0 | &render_plane, &my); |
821 | 0 | if (code < 0) |
822 | 0 | break; |
823 | 0 | lines_rasterized = min(code, line_count); |
824 | 0 | band_rect.p.y = my; |
825 | 0 | band_rect.q.y = my + lines_rasterized; |
826 | 0 | code = dev_proc(bdev, get_bits_rectangle) |
827 | 0 | (bdev, &band_rect, &band_params); |
828 | 0 | if (code < 0) |
829 | 0 | break; |
830 | 0 | params->options = options = band_params.options; |
831 | 0 | if (lines_rasterized == line_count) |
832 | 0 | break; |
833 | 0 | } |
834 | 0 | cdev->buf_procs.destroy_buf_device(bdev); |
835 | 0 | } |
836 | 0 | return code; |
837 | 0 | } |
838 | | |
839 | | /* Copy scan lines to the client. This is where rendering gets done. */ |
840 | | /* Processes min(requested # lines, # lines available thru end of band) */ |
841 | | int /* returns -ve error code, or # scan lines copied */ |
842 | | clist_rasterize_lines(gx_device *dev, int y, int line_count, |
843 | | gx_device *bdev, const gx_render_plane_t *render_plane, |
844 | | int *pmy) |
845 | 2.57M | { |
846 | 2.57M | gx_device_clist * const cldev = (gx_device_clist *)dev; |
847 | 2.57M | gx_device_clist_reader * const crdev = &cldev->reader; |
848 | 2.57M | gx_device *target = crdev->target; |
849 | 2.57M | uint raster = clist_plane_raster(target, render_plane); |
850 | 2.57M | byte *mdata = crdev->data + crdev->page_tile_cache_size; |
851 | 2.57M | byte *mlines = (crdev->page_line_ptrs_offset == 0 ? NULL : mdata + crdev->page_line_ptrs_offset); |
852 | 2.57M | int plane_index = (render_plane ? render_plane->index : -1); |
853 | 2.57M | int code; |
854 | | |
855 | | /* Render a band if necessary, and copy it incrementally. */ |
856 | 2.57M | if (crdev->ymin < 0 || crdev->yplane.index != plane_index || |
857 | 2.57M | !(y >= crdev->ymin && y < crdev->ymax) |
858 | 2.57M | ) { |
859 | 56.5k | int band_height = crdev->page_band_height; |
860 | 56.5k | int band = y / band_height; |
861 | 56.5k | int band_begin_line = band * band_height; |
862 | 56.5k | int band_end_line = band_begin_line + band_height; |
863 | 56.5k | int band_num_lines; |
864 | 56.5k | gs_int_rect band_rect; |
865 | | |
866 | 56.5k | if (band_end_line > dev->height) |
867 | 1.16k | band_end_line = dev->height; |
868 | | /* Clip line_count to current band */ |
869 | 56.5k | if (line_count > band_end_line - y) |
870 | 0 | line_count = band_end_line - y; |
871 | 56.5k | band_num_lines = band_end_line - band_begin_line; |
872 | | |
873 | 56.5k | if (y < 0 || y > dev->height) |
874 | 0 | return_error(gs_error_rangecheck); |
875 | 56.5k | code = crdev->buf_procs.setup_buf_device |
876 | 56.5k | (bdev, mdata, raster, (byte **)mlines, 0, band_num_lines, band_num_lines); |
877 | 56.5k | band_rect.p.x = 0; |
878 | 56.5k | band_rect.p.y = band_begin_line; |
879 | 56.5k | band_rect.q.x = dev->width; |
880 | 56.5k | band_rect.q.y = band_end_line; |
881 | 56.5k | if (code >= 0) |
882 | 56.5k | code = clist_render_rectangle(cldev, &band_rect, bdev, render_plane, |
883 | 56.5k | true); |
884 | | /* Reset the band boundaries now, so that we don't get */ |
885 | | /* an infinite loop. */ |
886 | 56.5k | crdev->ymin = band_begin_line; |
887 | 56.5k | crdev->ymax = band_end_line; |
888 | 56.5k | crdev->offset_map = NULL; |
889 | 56.5k | if (code < 0) |
890 | 6 | return code; |
891 | 56.5k | } |
892 | | |
893 | 2.57M | if (line_count > crdev->ymax - y) |
894 | 0 | line_count = crdev->ymax - y; |
895 | 2.57M | code = crdev->buf_procs.setup_buf_device |
896 | 2.57M | (bdev, mdata, raster, (byte **)mlines, y - crdev->ymin, line_count, |
897 | 2.57M | crdev->ymax - crdev->ymin); |
898 | 2.57M | if (code < 0) |
899 | 0 | return code; |
900 | | |
901 | 2.57M | *pmy = 0; |
902 | 2.57M | return line_count; |
903 | 2.57M | } |
904 | | |
905 | | /* |
906 | | * Render a rectangle to a client-supplied device. There is no necessary |
907 | | * relationship between band boundaries and the region being rendered. |
908 | | */ |
909 | | int |
910 | | clist_render_rectangle(gx_device_clist *cldev, const gs_int_rect *prect, |
911 | | gx_device *bdev, |
912 | | const gx_render_plane_t *render_plane, bool clear) |
913 | 56.5k | { |
914 | 56.5k | gx_device_clist_reader * const crdev = &cldev->reader; |
915 | 56.5k | const gx_placed_page *ppages; |
916 | 56.5k | int num_pages = crdev->num_pages; |
917 | 56.5k | int band_height = crdev->page_band_height; |
918 | 56.5k | int band_first = prect->p.y / band_height; |
919 | 56.5k | int band_last = (prect->q.y - 1) / band_height; |
920 | 56.5k | gx_band_page_info_t *pinfo; |
921 | 56.5k | gx_band_page_info_t page_info; |
922 | 56.5k | int code = 0; |
923 | 56.5k | int i; |
924 | 56.5k | bool save_pageneutralcolor; |
925 | | |
926 | 56.5k | if (render_plane) |
927 | 56.5k | crdev->yplane = *render_plane; |
928 | 0 | else |
929 | 0 | crdev->yplane.index = -1; |
930 | 56.5k | if_debug2m('l', bdev->memory, "[l]rendering bands (%d,%d)\n", band_first, band_last); |
931 | | |
932 | 56.5k | ppages = crdev->pages; |
933 | | |
934 | | /* Before playing back the clist, make sure that the gray detection is disabled */ |
935 | | /* so we don't slow down the rendering (primarily high level images). */ |
936 | 56.5k | save_pageneutralcolor = crdev->icc_struct->pageneutralcolor; |
937 | 56.5k | crdev->icc_struct->pageneutralcolor = false; |
938 | | |
939 | 113k | for (i = 0; i < num_pages && code >= 0; ++i) { |
940 | 56.5k | bool pdf14_needed = false; |
941 | 56.5k | int band; |
942 | | |
943 | 56.5k | if (ppages == NULL) { |
944 | | /* |
945 | | * If we aren't rendering saved pages, do the current one. |
946 | | * Note that this is the only case in which we may encounter |
947 | | * a gx_saved_page with non-zero cfile or bfile. |
948 | | */ |
949 | 56.5k | bdev->band_offset_x = 0; |
950 | 56.5k | bdev->band_offset_y = band_first * (long)band_height; |
951 | 56.5k | pinfo = &(crdev->page_info); |
952 | 56.5k | } else { |
953 | 0 | const gx_placed_page *ppage = &ppages[i]; |
954 | | |
955 | | /* Store the page information. */ |
956 | 0 | page_info.cfile = page_info.bfile = NULL; |
957 | 0 | strncpy(page_info.cfname, ppage->page->cfname, sizeof(page_info.cfname)-1); |
958 | 0 | strncpy(page_info.bfname, ppage->page->bfname, sizeof(page_info.bfname)-1); |
959 | 0 | page_info.io_procs = ppage->page->io_procs; |
960 | 0 | page_info.tile_cache_size = ppage->page->tile_cache_size; |
961 | 0 | page_info.bfile_end_pos = ppage->page->bfile_end_pos; |
962 | 0 | page_info.band_params = ppage->page->band_params; |
963 | 0 | page_info.line_ptrs_offset = 0; |
964 | 0 | pinfo = &page_info; |
965 | | |
966 | | /* |
967 | | * Set the band_offset_? values in case the buffer device |
968 | | * needs this. Example, a device may need to adjust the |
969 | | * phase of the dithering based on the page position, NOT |
970 | | * the position within the band buffer to avoid band stitch |
971 | | * lines in the dither pattern. The old wtsimdi device did this |
972 | | * |
973 | | * The band_offset_x is not important for placed pages that |
974 | | * are nested on a 'master' page (imposition) since each |
975 | | * page expects to be dithered independently, but setting |
976 | | * this allows pages to be contiguous without a dithering |
977 | | * shift. |
978 | | * |
979 | | * The following sets the band_offset_? relative to the |
980 | | * master page. |
981 | | */ |
982 | 0 | bdev->band_offset_x = ppage->offset.x; |
983 | 0 | bdev->band_offset_y = ppage->offset.y + (band_first * band_height); |
984 | 0 | } |
985 | | /* if any of the requested bands need transparency, use it for all of them */ |
986 | | /* The pdf14_ok_to_optimize checks if the target device (bdev) is compatible */ |
987 | | /* with the pdf14 compositor info that was written to the clist: colorspace, */ |
988 | | /* colorspace, etc. */ |
989 | 56.5k | pdf14_needed = !pdf14_ok_to_optimize(bdev); |
990 | 56.5k | for (band=band_first; !pdf14_needed && band <= band_last; band++) |
991 | 0 | pdf14_needed |= (crdev->color_usage_array[band].trans_bbox.p.y <= |
992 | 0 | crdev->color_usage_array[band].trans_bbox.q.y) ? true : false; |
993 | | |
994 | 56.5k | code = clist_playback_file_bands(pdf14_needed ? |
995 | 56.5k | playback_action_render : playback_action_render_no_pdf14, |
996 | 56.5k | crdev, pinfo, |
997 | 56.5k | bdev, band_first, band_last, |
998 | 56.5k | prect->p.x - bdev->band_offset_x, |
999 | 56.5k | prect->p.y); |
1000 | 56.5k | } |
1001 | 56.5k | crdev->icc_struct->pageneutralcolor = save_pageneutralcolor; /* restore it */ |
1002 | 56.5k | return code; |
1003 | 56.5k | } |
1004 | | |
1005 | | /* Playback the band file, taking the indicated action w/ its contents. */ |
1006 | | int |
1007 | | clist_playback_file_bands(clist_playback_action action, |
1008 | | gx_device_clist_reader *crdev, |
1009 | | gx_band_page_info_t *page_info, gx_device *target, |
1010 | | int band_first, int band_last, int x0, int y0) |
1011 | 56.5k | { |
1012 | 56.5k | int code = 0; |
1013 | 56.5k | bool opened_bfile = false; |
1014 | 56.5k | bool opened_cfile = false; |
1015 | | |
1016 | | /* We have to pick some allocator for rendering.... */ |
1017 | 56.5k | gs_memory_t *mem =crdev->memory; |
1018 | | |
1019 | 56.5k | stream_band_read_state rs; |
1020 | | |
1021 | | /* setup stream */ |
1022 | 56.5k | s_init_state((stream_state *)&rs, &s_band_read_template, |
1023 | 56.5k | (gs_memory_t *)0); /* Not mem, as we don't want to free rs */ |
1024 | 56.5k | rs.band_first = band_first; |
1025 | 56.5k | rs.band_last = band_last; |
1026 | 56.5k | rs.page_info = *page_info; |
1027 | 56.5k | rs.local_memory = mem; |
1028 | | |
1029 | | /* If this is a saved page, open the files. */ |
1030 | 56.5k | if (rs.page_cfile == 0) { |
1031 | 0 | code = crdev->page_info.io_procs->fopen(rs.page_cfname, |
1032 | 0 | gp_fmode_rb, &rs.page_cfile, crdev->bandlist_memory, |
1033 | 0 | crdev->bandlist_memory, true); |
1034 | 0 | opened_cfile = (code >= 0); |
1035 | 0 | } |
1036 | 56.5k | if (rs.page_bfile == 0 && code >= 0) { |
1037 | 0 | code = crdev->page_info.io_procs->fopen(rs.page_bfname, |
1038 | 0 | gp_fmode_rb, &rs.page_bfile, crdev->bandlist_memory, |
1039 | 0 | crdev->bandlist_memory, false); |
1040 | 0 | opened_bfile = (code >= 0); |
1041 | 0 | } |
1042 | 56.5k | if (rs.page_cfile != 0 && rs.page_bfile != 0) { |
1043 | 56.5k | stream s; |
1044 | 56.5k | byte sbuf[cbuf_size]; |
1045 | 56.5k | static const stream_procs no_procs = { |
1046 | 56.5k | s_std_noavailable, s_std_noseek, s_std_read_reset, |
1047 | 56.5k | s_std_read_flush, s_std_close, s_band_read_process |
1048 | 56.5k | }; |
1049 | | |
1050 | 56.5k | s_band_read_init((stream_state *)&rs); |
1051 | | # ifdef DEBUG |
1052 | | s_band_read_init_offset_map(crdev, (stream_state *)&rs); |
1053 | | # endif |
1054 | | /* The stream doesn't need a memory, but we'll need to access s.memory->gs_lib_ctx. */ |
1055 | 56.5k | s_init(&s, mem); |
1056 | 56.5k | s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read); |
1057 | 56.5k | s.foreign = 1; |
1058 | 56.5k | s.state = (stream_state *)&rs; |
1059 | | |
1060 | 56.5k | code = clist_playback_band(action, crdev, &s, target, x0, y0, mem); |
1061 | | # ifdef DEBUG |
1062 | | s_band_read_dnit_offset_map(crdev, (stream_state *)&rs); |
1063 | | # endif |
1064 | 56.5k | } |
1065 | | |
1066 | | /* Close the files if we just opened them. */ |
1067 | 56.5k | if (opened_bfile && rs.page_bfile != 0) |
1068 | 0 | crdev->page_info.io_procs->fclose(rs.page_bfile, rs.page_bfname, false); |
1069 | 56.5k | if (opened_cfile && rs.page_cfile != 0) |
1070 | 0 | crdev->page_info.io_procs->fclose(rs.page_cfile, rs.page_cfname, false); |
1071 | | |
1072 | 56.5k | return code; |
1073 | 56.5k | } |