xref: /plan9/sys/src/cmd/gs/src/gxclread.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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 = &current_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