1 /* Copyright (C) 1991, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gxclread.c,v 1.13 2004/10/07 05:18:34 ray Exp $ */
18 /* Command list reading for Ghostscript. */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gp.h" /* for gp_fmode_rb */
22 #include "gpcheck.h"
23 #include "gserrors.h"
24 #include "gxdevice.h"
25 #include "gscoord.h" /* requires gsmatrix.h */
26 #include "gsdevice.h" /* for gs_deviceinitialmatrix */
27 #include "gxdevmem.h" /* must precede gxcldev.h */
28 #include "gxcldev.h"
29 #include "gxgetbit.h"
30 #include "gxhttile.h"
31 #include "gdevplnx.h"
32 /*
33 * We really don't like the fact that gdevprn.h is included here, since
34 * command lists are supposed to be usable for purposes other than printer
35 * devices; but gdev_prn_colors_used and gdev_create_buf_device are
36 * currently only applicable to printer devices.
37 */
38 #include "gdevprn.h"
39 #include "stream.h"
40 #include "strimpl.h"
41
42 /* ------ Band file reading stream ------ */
43
44 /*
45 * To separate banding per se from command list interpretation,
46 * we make the command list interpreter simply read from a stream.
47 * When we are actually doing banding, the stream filters the band file
48 * and only passes through the commands for the current bands (or band
49 * ranges that include a current band).
50 */
51 typedef struct stream_band_read_state_s {
52 stream_state_common;
53 gx_band_page_info_t page_info;
54 int band_first, band_last;
55 uint left; /* amount of data left in this run */
56 cmd_block b_this;
57 } stream_band_read_state;
58
59 private int
s_band_read_init(stream_state * st)60 s_band_read_init(stream_state * st)
61 {
62 stream_band_read_state *const ss = (stream_band_read_state *) st;
63
64 ss->left = 0;
65 ss->b_this.band_min = 0;
66 ss->b_this.band_max = 0;
67 ss->b_this.pos = 0;
68 clist_rewind(ss->page_bfile, false, ss->page_bfname);
69 return 0;
70 }
71
72 private int
s_band_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)73 s_band_read_process(stream_state * st, stream_cursor_read * ignore_pr,
74 stream_cursor_write * pw, bool last)
75 {
76 stream_band_read_state *const ss = (stream_band_read_state *) st;
77 register byte *q = pw->ptr;
78 byte *wlimit = pw->limit;
79 clist_file_ptr cfile = ss->page_cfile;
80 clist_file_ptr bfile = ss->page_bfile;
81 uint left = ss->left;
82 int status = 1;
83 uint count;
84
85 while ((count = wlimit - q) != 0) {
86 if (left) { /* Read more data for the current run. */
87 if (count > left)
88 count = left;
89 clist_fread_chars(q + 1, count, cfile);
90 if (clist_ferror_code(cfile) < 0) {
91 status = ERRC;
92 break;
93 }
94 q += count;
95 left -= count;
96 process_interrupts(st->memory);
97 continue;
98 }
99 rb:
100 /*
101 * Scan for the next run for the current bands (or a band range
102 * that includes a current band).
103 */
104 if (ss->b_this.band_min == cmd_band_end &&
105 clist_ftell(bfile) == ss->page_bfile_end_pos
106 ) {
107 status = EOFC;
108 break;
109 } {
110 int bmin = ss->b_this.band_min;
111 int bmax = ss->b_this.band_max;
112 long pos = ss->b_this.pos;
113
114 clist_fread_chars(&ss->b_this, sizeof(ss->b_this), bfile);
115 if (!(ss->band_last >= bmin && ss->band_first <= bmax))
116 goto rb;
117 clist_fseek(cfile, pos, SEEK_SET, ss->page_cfname);
118 left = (uint) (ss->b_this.pos - pos);
119 if_debug5('l', "[l]reading for bands (%d,%d) at bfile %ld, cfile %ld, length %u\n",
120 bmin, bmax,
121 clist_ftell(bfile) - 2 * sizeof(ss->b_this),
122 pos, left);
123 }
124 }
125 pw->ptr = q;
126 ss->left = left;
127 return status;
128 }
129
130 /* Stream template */
131 private const stream_template s_band_read_template = {
132 &st_stream_state, s_band_read_init, s_band_read_process, 1, cbuf_size
133 };
134
135
136 /* ------ Reading/rendering ------ */
137
138 /* Forward references */
139
140 private int clist_render_init(gx_device_clist *);
141 private int clist_playback_file_bands(clist_playback_action action,
142 gx_device_clist_reader *cdev,
143 gx_band_page_info_t *page_info,
144 gx_device *target,
145 int band_first, int band_last,
146 int x0, int y0);
147 private int clist_rasterize_lines(gx_device *dev, int y, int lineCount,
148 gx_device *bdev,
149 const gx_render_plane_t *render_plane,
150 int *pmy);
151
152 /* Calculate the raster for a chunky or planar device. */
153 private int
clist_plane_raster(const gx_device * dev,const gx_render_plane_t * render_plane)154 clist_plane_raster(const gx_device *dev, const gx_render_plane_t *render_plane)
155 {
156 return bitmap_raster(dev->width *
157 (render_plane && render_plane->index >= 0 ?
158 render_plane->depth : dev->color_info.depth));
159 }
160
161 /* Select full-pixel rendering if required for RasterOp. */
162 private void
clist_select_render_plane(gx_device * dev,int y,int height,gx_render_plane_t * render_plane,int index)163 clist_select_render_plane(gx_device *dev, int y, int height,
164 gx_render_plane_t *render_plane, int index)
165 {
166 if (index >= 0) {
167 gx_colors_used_t colors_used;
168 int ignore_start;
169
170 gdev_prn_colors_used(dev, y, height, &colors_used, &ignore_start);
171 if (colors_used.slow_rop)
172 index = -1;
173 }
174 if (index < 0)
175 render_plane->index = index;
176 else
177 gx_render_plane_init(render_plane, dev, index);
178 }
179
180 /*
181 * Do device setup from params stored in command list. This is only for
182 * async rendering & assumes that the first command in every command list
183 * is a put_params command which sets all space-related parameters to the
184 * value they will have for the duration of that command list.
185 */
186 int
clist_setup_params(gx_device * dev)187 clist_setup_params(gx_device *dev)
188 {
189 gx_device_clist_reader * const crdev =
190 &((gx_device_clist *)dev)->reader;
191 int code = clist_render_init((gx_device_clist *)dev);
192 if (code < 0)
193 return code;
194
195 code = clist_playback_file_bands(playback_action_setup,
196 crdev, &crdev->page_info, 0, 0, 0, 0, 0);
197
198 /* put_params may have reinitialized device into a writer */
199 clist_render_init((gx_device_clist *)dev);
200
201 return code;
202 }
203
204 /* Copy a rasterized rectangle to the client, rasterizing if needed. */
205 int
clist_get_bits_rectangle(gx_device * dev,const gs_int_rect * prect,gs_get_bits_params_t * params,gs_int_rect ** unread)206 clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect,
207 gs_get_bits_params_t *params, gs_int_rect **unread)
208 {
209 gx_device_clist_common *cdev = (gx_device_clist_common *)dev;
210 gs_get_bits_options_t options = params->options;
211 int y = prect->p.y;
212 int end_y = prect->q.y;
213 int line_count = end_y - y;
214 gs_int_rect band_rect;
215 int lines_rasterized;
216 gx_device *bdev;
217 int num_planes =
218 (options & GB_PACKING_CHUNKY ? 1 :
219 options & GB_PACKING_PLANAR ? dev->color_info.num_components :
220 options & GB_PACKING_BIT_PLANAR ? dev->color_info.depth :
221 0 /****** NOT POSSIBLE ******/);
222 gx_render_plane_t render_plane;
223 int plane_index;
224 int my;
225 int code;
226
227 if (prect->p.x < 0 || prect->q.x > dev->width ||
228 y < 0 || end_y > dev->height
229 )
230 return_error(gs_error_rangecheck);
231 if (line_count <= 0 || prect->p.x >= prect->q.x)
232 return 0;
233
234 /*
235 * Calculate the render_plane from the params. There are two cases:
236 * full pixels, or a single plane.
237 */
238 plane_index = -1;
239 if (options & GB_SELECT_PLANES) {
240 /* Look for the one selected plane. */
241 int i;
242
243 for (i = 0; i < num_planes; ++i)
244 if (params->data[i]) {
245 if (plane_index >= 0) /* >1 plane requested */
246 return gx_default_get_bits_rectangle(dev, prect, params,
247 unread);
248 plane_index = i;
249 }
250 }
251 clist_select_render_plane(dev, y, line_count, &render_plane, plane_index);
252 code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
253 &bdev, cdev->target, &render_plane,
254 dev->memory, true);
255 if (code < 0)
256 return code;
257 code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my);
258 if (code < 0)
259 return code;
260 lines_rasterized = min(code, line_count);
261 /* Return as much of the rectangle as falls within the rasterized lines. */
262 band_rect = *prect;
263 band_rect.p.y = my;
264 band_rect.q.y = my + lines_rasterized;
265 code = dev_proc(bdev, get_bits_rectangle)
266 (bdev, &band_rect, params, unread);
267 cdev->buf_procs.destroy_buf_device(bdev);
268 if (code < 0 || lines_rasterized == line_count)
269 return code;
270 /*
271 * We'll have to return the rectangle in pieces. Force GB_RETURN_COPY
272 * rather than GB_RETURN_POINTER, and require all subsequent pieces to
273 * use the same values as the first piece for all of the other format
274 * options. If copying isn't allowed, or if there are any unread
275 * rectangles, punt.
276 */
277 if (!(options & GB_RETURN_COPY) || code > 0)
278 return gx_default_get_bits_rectangle(dev, prect, params, unread);
279 options = params->options;
280 if (!(options & GB_RETURN_COPY)) {
281 /* Redo the first piece with copying. */
282 params->options = options =
283 (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY;
284 lines_rasterized = 0;
285 }
286 {
287 gs_get_bits_params_t band_params;
288 uint raster = gx_device_raster(bdev, true);
289
290 code = gdev_create_buf_device(cdev->buf_procs.create_buf_device,
291 &bdev, cdev->target, &render_plane,
292 dev->memory, true);
293 if (code < 0)
294 return code;
295 band_params = *params;
296 while ((y += lines_rasterized) < end_y) {
297 int i;
298
299 /* Increment data pointers by lines_rasterized. */
300 for (i = 0; i < num_planes; ++i)
301 if (band_params.data[i])
302 band_params.data[i] += raster * lines_rasterized;
303 line_count = end_y - y;
304 code = clist_rasterize_lines(dev, y, line_count, bdev,
305 &render_plane, &my);
306 if (code < 0)
307 break;
308 lines_rasterized = min(code, line_count);
309 band_rect.p.y = my;
310 band_rect.q.y = my + lines_rasterized;
311 code = dev_proc(bdev, get_bits_rectangle)
312 (bdev, &band_rect, &band_params, unread);
313 if (code < 0)
314 break;
315 params->options = options = band_params.options;
316 if (lines_rasterized == line_count)
317 break;
318 }
319 cdev->buf_procs.destroy_buf_device(bdev);
320 }
321 return code;
322 }
323
324 /* Copy scan lines to the client. This is where rendering gets done. */
325 /* Processes min(requested # lines, # lines available thru end of band) */
326 private int /* returns -ve error code, or # scan lines copied */
clist_rasterize_lines(gx_device * dev,int y,int line_count,gx_device * bdev,const gx_render_plane_t * render_plane,int * pmy)327 clist_rasterize_lines(gx_device *dev, int y, int line_count,
328 gx_device *bdev, const gx_render_plane_t *render_plane,
329 int *pmy)
330 {
331 gx_device_clist * const cdev = (gx_device_clist *)dev;
332 gx_device_clist_reader * const crdev = &cdev->reader;
333 gx_device *target = crdev->target;
334 uint raster = clist_plane_raster(target, render_plane);
335 byte *mdata = crdev->data + crdev->page_tile_cache_size;
336 int plane_index = (render_plane ? render_plane->index : -1);
337 int code;
338
339 /* Render a band if necessary, and copy it incrementally. */
340 if (crdev->ymin < 0 || crdev->yplane.index != plane_index ||
341 !(y >= crdev->ymin && y < crdev->ymax)
342 ) {
343 int band_height = crdev->page_band_height;
344 int band = y / band_height;
345 int band_begin_line = band * band_height;
346 int band_end_line = band_begin_line + band_height;
347 int band_num_lines;
348 gs_int_rect band_rect;
349
350 if (band_end_line > dev->height)
351 band_end_line = dev->height;
352 /* Clip line_count to current band */
353 if (line_count > band_end_line - y)
354 line_count = band_end_line - y;
355 band_num_lines = band_end_line - band_begin_line;
356
357 if (y < 0 || y > dev->height)
358 return_error(gs_error_rangecheck);
359 code = crdev->buf_procs.setup_buf_device
360 (bdev, mdata, raster, NULL, 0, band_num_lines, band_num_lines);
361 band_rect.p.x = 0;
362 band_rect.p.y = band_begin_line;
363 band_rect.q.x = dev->width;
364 band_rect.q.y = band_end_line;
365 if (code >= 0)
366 code = clist_render_rectangle(cdev, &band_rect, bdev, render_plane,
367 true);
368 /* Reset the band boundaries now, so that we don't get */
369 /* an infinite loop. */
370 crdev->ymin = band_begin_line;
371 crdev->ymax = band_end_line;
372 if (code < 0)
373 return code;
374 }
375
376 if (line_count > crdev->ymax - y)
377 line_count = crdev->ymax - y;
378 code = crdev->buf_procs.setup_buf_device
379 (bdev, mdata, raster, NULL, y - crdev->ymin, line_count,
380 crdev->ymax - crdev->ymin);
381 if (code < 0)
382 return code;
383
384 *pmy = 0;
385 return line_count;
386 }
387
388 /* Initialize for reading. */
389 private int
clist_render_init(gx_device_clist * dev)390 clist_render_init(gx_device_clist *dev)
391 {
392 gx_device_clist_reader * const crdev = &dev->reader;
393
394 crdev->ymin = crdev->ymax = 0;
395 crdev->yplane.index = -1;
396 /* For normal rasterizing, pages and num_pages are zero. */
397 crdev->pages = 0;
398 crdev->num_pages = 0;
399 return 0;
400 }
401
402 /*
403 * Render a rectangle to a client-supplied device. There is no necessary
404 * relationship between band boundaries and the region being rendered.
405 */
406 int
clist_render_rectangle(gx_device_clist * cdev,const gs_int_rect * prect,gx_device * bdev,const gx_render_plane_t * render_plane,bool clear)407 clist_render_rectangle(gx_device_clist *cdev, const gs_int_rect *prect,
408 gx_device *bdev,
409 const gx_render_plane_t *render_plane, bool clear)
410 {
411 gx_device_clist_reader * const crdev = &cdev->reader;
412 const gx_placed_page *ppages;
413 int num_pages = crdev->num_pages;
414 int band_height = crdev->page_band_height;
415 int band_first = prect->p.y / band_height;
416 int band_last = (prect->q.y - 1) / band_height;
417 gx_saved_page current_page;
418 gx_placed_page placed_page;
419 int code = 0;
420 int i;
421
422 /* Initialize for rendering if we haven't done so yet. */
423 if (crdev->ymin < 0) {
424 code = clist_end_page(&cdev->writer);
425 if (code < 0)
426 return code;
427 code = clist_render_init(cdev);
428 if (code < 0)
429 return code;
430 }
431 if (render_plane)
432 crdev->yplane = *render_plane;
433 else
434 crdev->yplane.index = -1;
435 if_debug2('l', "[l]rendering bands (%d,%d)\n", band_first, band_last);
436 if (clear)
437 dev_proc(bdev, fill_rectangle)
438 (bdev, 0, 0, bdev->width, bdev->height, gx_device_white(bdev));
439
440 /*
441 * If we aren't rendering saved pages, do the current one.
442 * Note that this is the only case in which we may encounter
443 * a gx_saved_page with non-zero cfile or bfile.
444 */
445 ppages = crdev->pages;
446 if (ppages == 0) {
447 current_page.info = crdev->page_info;
448 placed_page.page = ¤t_page;
449 placed_page.offset.x = placed_page.offset.y = 0;
450 ppages = &placed_page;
451 num_pages = 1;
452 }
453 for (i = 0; i < num_pages && code >= 0; ++i) {
454 const gx_placed_page *ppage = &ppages[i];
455
456 code = clist_playback_file_bands(playback_action_render,
457 crdev, &ppage->page->info,
458 bdev, band_first, band_last,
459 prect->p.x - ppage->offset.x,
460 prect->p.y);
461 }
462 return code;
463 }
464
465 /* Playback the band file, taking the indicated action w/ its contents. */
466 private int
clist_playback_file_bands(clist_playback_action action,gx_device_clist_reader * cdev,gx_band_page_info_t * page_info,gx_device * target,int band_first,int band_last,int x0,int y0)467 clist_playback_file_bands(clist_playback_action action,
468 gx_device_clist_reader *cdev,
469 gx_band_page_info_t *page_info, gx_device *target,
470 int band_first, int band_last, int x0, int y0)
471 {
472 int code = 0;
473 bool opened_bfile = false;
474 bool opened_cfile = false;
475
476 /* We have to pick some allocator for rendering.... */
477 gs_memory_t *mem =cdev->memory;
478
479 stream_band_read_state rs;
480
481 /* setup stream */
482 s_init_state((stream_state *)&rs, &s_band_read_template,
483 (gs_memory_t *)0);
484 rs.band_first = band_first;
485 rs.band_last = band_last;
486 rs.page_info = *page_info;
487
488 /* If this is a saved page, open the files. */
489 if (rs.page_cfile == 0) {
490 code = clist_fopen(rs.page_cfname,
491 gp_fmode_rb, &rs.page_cfile, cdev->bandlist_memory,
492 cdev->bandlist_memory, true);
493 opened_cfile = (code >= 0);
494 }
495 if (rs.page_bfile == 0 && code >= 0) {
496 code = clist_fopen(rs.page_bfname,
497 gp_fmode_rb, &rs.page_bfile, cdev->bandlist_memory,
498 cdev->bandlist_memory, false);
499 opened_bfile = (code >= 0);
500 }
501 if (rs.page_cfile != 0 && rs.page_bfile != 0) {
502 stream s;
503 byte sbuf[cbuf_size];
504 static const stream_procs no_procs = {
505 s_std_noavailable, s_std_noseek, s_std_read_reset,
506 s_std_read_flush, s_std_close, s_band_read_process
507 };
508
509 s_band_read_init((stream_state *)&rs);
510 /* The stream doesn't need a memory, but we'll need to access s.memory->gs_lib_ctx. */
511 s_init(&s, mem);
512 s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read);
513 s.foreign = 1;
514 s.state = (stream_state *)&rs;
515 code = clist_playback_band(action, cdev, &s, target, x0, y0, mem);
516 }
517
518 /* Close the files if we just opened them. */
519 if (opened_bfile && rs.page_bfile != 0)
520 clist_fclose(rs.page_bfile, rs.page_bfname, false);
521 if (opened_cfile && rs.page_cfile != 0)
522 clist_fclose(rs.page_cfile, rs.page_cfname, false);
523
524 return code;
525 }
526