xref: /plan9/sys/src/cmd/gs/src/gdevbmpa.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 1999, 2000 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: gdevbmpa.c,v 1.6 2002/06/16 05:48:54 lpd Exp $ */
18 /* .BMP file format output drivers: Demo of ASYNC rendering */
19 
20 /* 2000-04-20 ghost@aladdin.com - Makes device structures const, changing
21    makefile entry from DEV to DEV2. */
22 /* 1998/12/29 ghost@aladdin.com - Modified to use gdev_prn_render_lines,
23    which replaces the former "overlay" calls */
24 /* 1998/11/23 ghost@aladdin.com - Removed pointless restriction to
25    single-page output */
26 /* 1998/7/28 ghost@aladdin.com - Factored out common BMP format code
27    to gdevbmpc.c */
28 /* Initial version 2/2/98 by John Desrosiers (soho@crl.com) */
29 
30 #include "stdio_.h"
31 #include "gserrors.h"
32 #include "gdevprna.h"
33 #include "gdevpccm.h"
34 #include "gdevbmp.h"
35 #include "gdevppla.h"
36 #include "gpsync.h"
37 
38 /*
39  * The original version of this driver was restricted to producing a single
40  * page per file.  If for some reason you want to reinstate this
41  * restriction, uncomment the next line.
42  * NOTE: Even though the logic for multi-page files is straightforward,
43  * it results in a file that most programs that process BMP format cannot
44  * handle. Most programs will only display the first page.
45  */
46 /*************** #define SINGLE_PAGE ****************/
47 
48 /* ------ The device descriptors ------ */
49 
50 /* Define data type for this device based on prn_device */
51 typedef struct gx_device_async_s {
52     gx_device_common;
53     gx_prn_device_common;
54     bool UsePlanarBuffer;
55     int buffered_page_exists;
56     long file_offset_to_data[4];
57 } gx_device_async;
58 
59 /* Define initializer for device */
60 #define async_device(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\
61 { prn_device_std_margins_body(gx_device_async, procs, dname,\
62     w10, h10, xdpi, ydpi, lm, tm, lm, bm, rm, tm, color_bits, print_page),\
63     0, 0, { 0, 0, 0, 0 }\
64 }
65 
66 private dev_proc_open_device(bmpa_writer_open);
67 private dev_proc_open_device(bmpa_cmyk_writer_open);
68 private prn_dev_proc_open_render_device(bmpa_reader_open_render_device);
69 private dev_proc_print_page_copies(bmpa_reader_print_page_copies);
70 /* VMS limits procedure names to 31 characters. */
71 private dev_proc_print_page_copies(bmpa_cmyk_reader_print_copies);
72 private prn_dev_proc_buffer_page(bmpa_reader_buffer_page);
73 private prn_dev_proc_buffer_page(bmpa_cmyk_reader_buffer_page);
74 private dev_proc_output_page(bmpa_reader_output_page);
75 private dev_proc_get_params(bmpa_get_params);
76 private dev_proc_put_params(bmpa_put_params);
77 private dev_proc_get_hardware_params(bmpa_get_hardware_params);
78 private prn_dev_proc_start_render_thread(bmpa_reader_start_render_thread);
79 private prn_dev_proc_get_space_params(bmpa_get_space_params);
80 #define default_print_page 0	/* not needed becoz print_page_copies def'd */
81 
82 /* Monochrome. */
83 
84 private const gx_device_procs bmpamono_procs =
85   prn_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close);
86 const gx_device_async gs_bmpamono_device =
87   async_device(bmpamono_procs, "bmpamono",
88 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
89 	X_DPI, Y_DPI,
90 	0,0,0,0,			/* margins */
91 	1, default_print_page);
92 
93 /* 1-bit-per-plane separated CMYK color. */
94 
95 #define bmpa_cmyk_procs(p_open, p_map_color_rgb, p_map_cmyk_color)\
96     p_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,\
97     NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
98     bmpa_get_params, bmpa_put_params,\
99     p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
100 
101 private const gx_device_procs bmpasep1_procs = {
102     bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_1bit_map_color_rgb,
103 		    cmyk_1bit_map_cmyk_color)
104 };
105 const gx_device_async gs_bmpasep1_device = {
106   prn_device_body(gx_device_async, bmpasep1_procs, "bmpasep1",
107 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
108 	X_DPI, Y_DPI,
109 	0,0,0,0,			/* margins */
110 	4, 4, 1, 1, 2, 2, default_print_page)
111 };
112 
113 /* 8-bit-per-plane separated CMYK color. */
114 
115 private const gx_device_procs bmpasep8_procs = {
116     bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_8bit_map_color_rgb,
117 		    cmyk_8bit_map_cmyk_color)
118 };
119 const gx_device_async gs_bmpasep8_device = {
120   prn_device_body(gx_device_async, bmpasep8_procs, "bmpasep8",
121 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
122 	X_DPI, Y_DPI,
123 	0,0,0,0,			/* margins */
124 	4, 32, 255, 255, 256, 256, default_print_page)
125 };
126 
127 /* 4-bit (EGA/VGA-style) color. */
128 
129 private const gx_device_procs bmpa16_procs =
130   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
131     pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
132 const gx_device_async gs_bmpa16_device =
133   async_device(bmpa16_procs, "bmpa16",
134 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
135 	X_DPI, Y_DPI,
136 	0,0,0,0,			/* margins */
137 	4, default_print_page);
138 
139 /* 8-bit (SuperVGA-style) color. */
140 /* (Uses a fixed palette of 3,3,2 bits.) */
141 
142 private const gx_device_procs bmpa256_procs =
143   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
144     pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
145 const gx_device_async gs_bmpa256_device =
146   async_device(bmpa256_procs, "bmpa256",
147 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
148 	X_DPI, Y_DPI,
149 	0,0,0,0,			/* margins */
150 	8, default_print_page);
151 
152 /* 24-bit color. */
153 
154 private const gx_device_procs bmpa16m_procs =
155   prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close,
156     bmp_map_16m_rgb_color, bmp_map_16m_color_rgb);
157 const gx_device_async gs_bmpa16m_device =
158   async_device(bmpa16m_procs, "bmpa16m",
159 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
160 	X_DPI, Y_DPI,
161 	0,0,0,0,			/* margins */
162 	24, default_print_page);
163 
164 /* 32-bit CMYK color (outside the BMP specification). */
165 
166 private const gx_device_procs bmpa32b_procs = {
167     bmpa_cmyk_procs(bmpa_writer_open, gx_default_map_color_rgb,
168 		    gx_default_cmyk_map_cmyk_color)
169 };
170 const gx_device_async gs_bmpa32b_device =
171   async_device(bmpa32b_procs, "bmpa32b",
172 	       DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
173 	       X_DPI, Y_DPI,
174 	       0, 0, 0, 0,		/* margins */
175 	       32, default_print_page);
176 
177 /* --------- Forward declarations ---------- */
178 
179 private void bmpa_reader_thread(void *);
180 
181 /* ------------ Writer Instance procedures ---------- */
182 
183 /* Writer's open procedure */
184 private int
bmpa_open_writer(gx_device * pdev,dev_proc_print_page_copies ((* reader_print_page_copies)),prn_dev_proc_buffer_page ((* reader_buffer_page)))185 bmpa_open_writer(gx_device *pdev  /* Driver instance to open */,
186 		 dev_proc_print_page_copies((*reader_print_page_copies)),
187 		 prn_dev_proc_buffer_page((*reader_buffer_page)))
188 {
189     gx_device_async * const pwdev = (gx_device_async *)pdev;
190     int max_width;
191     int max_raster;
192     int min_band_height;
193     int max_src_image_row;
194 
195     /*
196      * Set up device's printer proc vector to point to this driver, since
197      * there are no convenient macros for setting them up in static template.
198      */
199     init_async_render_procs(pwdev, bmpa_reader_start_render_thread,
200 			    reader_buffer_page,
201 			    reader_print_page_copies);
202     set_dev_proc(pdev, get_params, bmpa_get_params);	/* because not all device-init macros allow this to be defined */
203     set_dev_proc(pdev, put_params, bmpa_put_params);	/* ibid. */
204     set_dev_proc(pdev, get_hardware_params, bmpa_get_hardware_params);
205     set_dev_proc(pdev, output_page, bmpa_reader_output_page);	/* hack */
206     pwdev->printer_procs.get_space_params = bmpa_get_space_params;
207     pwdev->printer_procs.open_render_device =
208 	bmpa_reader_open_render_device;	/* Included for tutorial value */
209 
210     /*
211      * Determine MAXIMUM parameters this device will have to support over
212      * lifetime.  See comments for bmpa_get_space_params().
213      */
214     max_width = DEFAULT_WIDTH_10THS * 60;   /* figure max wid = default @ 600dpi */
215     min_band_height = max(1, (DEFAULT_HEIGHT_10THS * 60) / 100);
216     max_raster = bitmap_raster(max_width * pwdev->color_info.depth);	/* doesn't need to be super accurate */
217     max_src_image_row = max_width * 4 * 2;
218 
219     /* Set to planar buffering mode if appropriate. */
220     if (pwdev->UsePlanarBuffer)
221 	gdev_prn_set_procs_planar(pdev);
222 
223     /* Special writer open routine for async interpretation */
224     /* Starts render thread */
225     return gdev_prn_async_write_open((gx_device_printer *)pdev,
226 				     max_raster, min_band_height,
227 				     max_src_image_row);
228 }
229 private int
bmpa_writer_open(gx_device * pdev)230 bmpa_writer_open(gx_device *pdev  /* Driver instance to open */)
231 {
232     return bmpa_open_writer(pdev, bmpa_reader_print_page_copies,
233 			    bmpa_reader_buffer_page);
234 }
235 private int
bmpa_cmyk_writer_open(gx_device * pdev)236 bmpa_cmyk_writer_open(gx_device *pdev  /* Driver instance to open */)
237 {
238     return bmpa_open_writer(pdev, bmpa_cmyk_reader_print_copies,
239 			    bmpa_cmyk_reader_buffer_page);
240 }
241 
242 /* -------------- Renderer instance procedures ----------*/
243 
244 /* Forward declarations */
245 private int
246     bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *prn_stream,
247 			      int num_copies, int first_plane,
248 			      int last_plane, int raster);
249 
250 /* Thread to do rendering, started by bmpa_reader_start_render_thread */
251 private void
bmpa_reader_thread(void * params)252 bmpa_reader_thread(void *params)
253 {
254     gdev_prn_async_render_thread((gdev_prn_start_render_params *)params);
255 }
256 
257 private int	/* rets 0 ok, -ve error if couldn't start thread */
bmpa_reader_start_render_thread(gdev_prn_start_render_params * params)258 bmpa_reader_start_render_thread(gdev_prn_start_render_params *params)
259 {
260     return gp_create_thread(bmpa_reader_thread, params);
261 }
262 
263 private int
bmpa_reader_open_render_device(gx_device_printer * ppdev)264 bmpa_reader_open_render_device(gx_device_printer *ppdev)
265 {
266     /*
267      * Do anything that needs to be done at open time here.
268      * Since this implementation doesn't do anything, we don't need to
269      * cast the device argument to the more specific type.
270      */
271     /*gx_device_async * const prdev = (gx_device_async *)ppdev;*/
272 
273     /* Cascade down to the default handler */
274     return gdev_prn_async_render_open(ppdev);
275 }
276 
277 /* Generic routine to send the page to the printer. */
278 private int
bmpa_reader_output_page(gx_device * pdev,int num_copies,int flush)279 bmpa_reader_output_page(gx_device *pdev, int num_copies, int flush)
280 {
281     /*
282      * HACK: open the printer page with the positionable attribute since
283      * we need to seek back & forth to support partial rendering.
284      */
285     if ( num_copies > 0 || !flush ) {
286 	int code = gdev_prn_open_printer_positionable(pdev, 1, 1);
287 
288 	if ( code < 0 )
289 	    return code;
290     }
291     return gdev_prn_output_page(pdev, num_copies, flush);
292 }
293 
294 private int
bmpa_reader_print_planes(gx_device_printer * pdev,FILE * prn_stream,int num_copies,int first_plane,int last_plane,int raster)295 bmpa_reader_print_planes(gx_device_printer *pdev, FILE *prn_stream,
296 			 int num_copies, int first_plane, int last_plane,
297 			 int raster)
298 {
299     gx_device_async * const prdev = (gx_device_async *)pdev;
300     /* BMP scan lines are padded to 32 bits. */
301     uint bmp_raster = raster + (-raster & 3);
302     int code = 0;
303     int y;
304     byte *row = 0;
305     byte *raster_data;
306     int plane;
307 
308     /* If there's data in buffer, need to process w/overlays */
309     if (prdev->buffered_page_exists) {
310 	code = bmpa_reader_buffer_planes(pdev, prn_stream, num_copies,
311 					 first_plane, last_plane, raster);
312 	goto done;
313     }
314 #ifdef SINGLE_PAGE
315     /* BMP format is single page, so discard all but 1st printable page */
316     /* Since the OutputFile may have a %d, we use ftell to determine if */
317     /* this is a zero length file, which is legal to write		*/
318     if (ftell(prn_stream) != 0)
319 	return 0;
320 #endif
321     row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
322     if (row == 0)		/* can't allocate row buffer */
323 	return_error(gs_error_VMerror);
324 
325     for (plane = first_plane; plane <= last_plane; ++plane) {
326 	gx_render_plane_t render_plane;
327 
328 	/* Write header & seek to its end */
329 	code =
330 	    (first_plane < 0 ? write_bmp_header(pdev, prn_stream) :
331 	     write_bmp_separated_header(pdev, prn_stream));
332 	if (code < 0)
333 	    goto done;
334 	/* Save the file offset where data begins */
335 	if ((prdev->file_offset_to_data[plane - first_plane] =
336 	     ftell(prn_stream)) == -1L) {
337 	    code = gs_note_error(gs_error_ioerror);
338 	    goto done;
339 	}
340 
341 	/*
342 	 * Write out the bands top to bottom.  Finish the job even if
343 	 * num_copies == 0, to avoid invalid output file.
344 	 */
345 	if (plane >= 0)
346 	    gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
347 	for (y = prdev->height - 1; y >= 0; y--) {
348 	    uint actual_raster;
349 
350 	    code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster,
351 				      &raster_data, &actual_raster,
352 				      (plane < 0 ? NULL : &render_plane));
353 	    if (code < 0)
354 		goto done;
355 	    if (fwrite((const char *)raster_data, actual_raster, 1, prn_stream) < 1) {
356 		code = gs_error_ioerror;
357 		goto done;
358 	    }
359 	}
360     }
361 done:
362     gs_free_object(pdev->memory, row, "bmp file buffer");
363     prdev->buffered_page_exists = 0;
364     return code;
365 }
366 private int
bmpa_reader_print_page_copies(gx_device_printer * pdev,FILE * prn_stream,int num_copies)367 bmpa_reader_print_page_copies(gx_device_printer *pdev, FILE *prn_stream,
368 			      int num_copies)
369 {
370     return bmpa_reader_print_planes(pdev, prn_stream, num_copies, -1, -1,
371 				    gdev_prn_raster(pdev));
372 }
373 private int
bmpa_cmyk_plane_raster(gx_device_printer * pdev)374 bmpa_cmyk_plane_raster(gx_device_printer *pdev)
375 {
376     return bitmap_raster(pdev->width * (pdev->color_info.depth / 4));
377 }
378 private int
bmpa_cmyk_reader_print_copies(gx_device_printer * pdev,FILE * prn_stream,int num_copies)379 bmpa_cmyk_reader_print_copies(gx_device_printer *pdev, FILE *prn_stream,
380 			      int num_copies)
381 {
382     return bmpa_reader_print_planes(pdev, prn_stream, num_copies, 0, 3,
383 				    bmpa_cmyk_plane_raster(pdev));
384 }
385 
386 /* Buffer a (partial) rasterized page & optionally print result multiple times. */
387 private int
bmpa_reader_buffer_planes(gx_device_printer * pdev,FILE * file,int num_copies,int first_plane,int last_plane,int raster)388 bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *file, int num_copies,
389 			  int first_plane, int last_plane, int raster)
390 {
391     gx_device_async * const prdev = (gx_device_async *)pdev;
392     gx_device * const dev = (gx_device *)pdev;
393     int code = 0;
394 
395     /* If there's no data in buffer, no need to do any overlays */
396     if (!prdev->buffered_page_exists) {
397 	code = bmpa_reader_print_planes(pdev, file, num_copies,
398 					first_plane, last_plane, raster);
399 	goto done;
400     }
401 
402     /*
403      * Continue rendering on top of the existing file. This requires setting
404      * up a buffer of the existing bits in GS's format (except for optional
405      * extra padding bytes at the end of each scan line, provided the scan
406      * lines are still correctly memory-aligned) and then calling
407      * gdev_prn_render_lines.  If the device already provides a band buffer
408      * -- which currently is always the case -- we can use it if we want;
409      * but if a device stores partially rendered pages in memory in a
410      * compatible format (e.g., a printer with a hardware page buffer), it
411      * can render directly on top of the stored bits.
412      *
413      * If we can render exactly one band (or N bands) at a time, this is
414      * more efficient, since otherwise (a) band(s) will have to be rendered
415      * more than once.
416      */
417 
418     {
419 	byte *raster_data;
420 	gx_device_clist_reader *const crdev =
421 	    (gx_device_clist_reader *)pdev;
422 	int raster = gx_device_raster(dev, 1);
423 	int padding = -raster & 3; /* BMP scan lines are padded to 32 bits. */
424 	int bmp_raster = raster + padding;
425 	int plane;
426 
427 	/*
428 	 * Get the address of the renderer's band buffer.  In the future,
429 	 * it will be possible to suppress the allocation of this buffer,
430 	 * and to use only buffers provided the driver itself (e.g., a
431 	 * hardware buffer).
432 	 */
433 	if (!pdev->buffer_space) {
434 	    /* Not banding.  Can't happen. */
435 	    code = gs_note_error(gs_error_Fatal);
436 	    goto done;
437 	}
438 	raster_data = crdev->data;
439 
440 	for (plane = first_plane; plane <= last_plane; ++plane) {
441 	    gx_render_plane_t render_plane;
442 	    gx_device *bdev;
443 	    int y, band_base_line;
444 
445 	    /* Seek to beginning of data portion of file */
446 	    if (fseek(file, prdev->file_offset_to_data[plane - first_plane],
447 		      SEEK_SET)) {
448 		code = gs_note_error(gs_error_ioerror);
449 		goto done;
450 	    }
451 
452 	    if (plane >= 0)
453 		gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
454 	    else
455 		render_plane.index = -1;
456 
457 	    /* Set up the buffer device. */
458 	    code = gdev_create_buf_device(crdev->buf_procs.create_buf_device,
459 					  &bdev, crdev->target, &render_plane,
460 					  dev->memory, true);
461 	    if (code < 0)
462 		goto done;
463 
464 	    /*
465 	     * Iterate thru bands from top to bottom.  As noted above, we
466 	     * do this an entire band at a time for efficiency.
467 	     */
468 	    for (y = dev->height - 1; y >= 0; y = band_base_line - 1) {
469 		int band_height =
470 		    dev_proc(dev, get_band)(dev, y, &band_base_line);
471 		int line;
472 		gs_int_rect band_rect;
473 
474 		/* Set up the buffer device for this band. */
475 		code = crdev->buf_procs.setup_buf_device
476 		    (bdev, raster_data, bmp_raster, NULL, 0, band_height,
477 		     band_height);
478 		if (code < 0)
479 		    goto done;
480 
481 		/* Fill in the buffer with a band from the BMP file. */
482 		/* Need to do this backward since BMP is top to bottom. */
483 		for (line = band_height - 1; line >= 0; --line)
484 		    if (fread(raster_data + line * bmp_raster,
485 			      raster, 1, file) < 1 ||
486 			fseek(file, padding, SEEK_CUR)
487 			) {
488 			code = gs_note_error(gs_error_ioerror);
489 			goto done;
490 		    }
491 
492 		/* Continue rendering on top of the existing bits. */
493 		band_rect.p.x = 0;
494 		band_rect.p.y = band_base_line;
495 		band_rect.q.x = pdev->width;
496 		band_rect.q.y = band_base_line + band_height;
497 		if ((code = clist_render_rectangle((gx_device_clist *)pdev,
498 						   &band_rect, bdev,
499 						   &render_plane, false)) < 0)
500 		    goto done;
501 
502 		/* Rewind & write out the updated buffer. */
503 		if (fseek(file, -bmp_raster * band_height, SEEK_CUR)) {
504 		    code = gs_note_error(gs_error_ioerror);
505 		    goto done;
506 		}
507 		for (line = band_height - 1; line >= 0; --line) {
508 		    if (fwrite(raster_data + line * bmp_raster,
509 			       bmp_raster, 1, file) < 1 ||
510 			fseek(file, padding, SEEK_CUR)
511 			) {
512 			code = gs_note_error(gs_error_ioerror);
513 			goto done;
514 		    }
515 		}
516 	    }
517 	    crdev->buf_procs.destroy_buf_device(bdev);
518 	}
519     }
520 
521  done:
522     prdev->buffered_page_exists = (code >= 0);
523     return code;
524 }
525 private int
bmpa_reader_buffer_page(gx_device_printer * pdev,FILE * prn_stream,int num_copies)526 bmpa_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
527 			int num_copies)
528 {
529     return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, -1, -1,
530 				     gdev_prn_raster(pdev));
531 }
532 private int
bmpa_cmyk_reader_buffer_page(gx_device_printer * pdev,FILE * prn_stream,int num_copies)533 bmpa_cmyk_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream,
534 			     int num_copies)
535 {
536     return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, 0, 3,
537 				     bmpa_cmyk_plane_raster(pdev));
538 }
539 
540 /*------------ Procedures common to writer & renderer -------- */
541 
542 /* Compute space parameters */
543 private void
bmpa_get_space_params(const gx_device_printer * pdev,gdev_prn_space_params * space_params)544 bmpa_get_space_params(const gx_device_printer *pdev,
545  gdev_prn_space_params *space_params)
546 {
547     /* Plug params into device before opening it
548      *
549      * You ask "How did you come up with these #'s?" You asked, so...
550      *
551      * To answer clearly, let me begin by recapitulating how command list
552      * (clist) device memory allocation works in the non-overlapped case:
553      * When the device is opened, a buffer is allocated. How big? For
554      * starters, it must be >= PRN_MIN_BUFFER_SPACE, and as we'll see, must
555      * be sufficient to satisfy the rest of the band params. If you don't
556      * specify a size for it in space_params.band.BandBufferSpace, the open
557      * routine will use a heuristic where it tries to use PRN_BUFFER_SPACE,
558      * then works its way down by factors of 2 if that much memory isn't
559      * available.
560      *
561      * The device proceeds to divide the buffer into several parts: one of
562      * them is used for the same thing during writing & rasterizing; the
563      * other parts are redivided and used differently writing and
564      * rasterizing. The limiting factor dictating memory requirements is the
565      * rasterizer's render buffer.  This buffer needs to be able to contain
566      * a pixmap that covers an entire band. Memory consumption is whatever
567      * is needed to hold N rows of data aligned on word boundaries, +
568      * sizeof(pointer) for each of N rows. Whatever is left over in the
569      * rasterized is allocated to a tile cache. You want to make sure that
570      * cache is at least 50KB.
571      *
572      * For example, take a 600 dpi b/w device at 8.5 x 11 inches.  For the
573      * whole device, that's 6600 rows @ 638 bytes = ~4.2 MB total.  If the
574      * device is divided into 100 bands, each band's rasterizer buffer is
575      * 62K. Add on a 50K tile cache, and you get a 112KB (+ add a little
576      * slop) total device buffer size.
577      *
578      * Now that we've covered the rasterizer, let's switch back to the
579      * writer. The writer must have a tile cache *exactly* the same size as
580      * the reader. This means that the space to divide up for the writer is
581      * equal is size to the rasterizer's band buffer.  This space is divided
582      * into 2 sections: per-band bookeeping info and a command buffer. The
583      * bookeeping info currently uses ~72 bytes for each band. The rest is
584      * the command buffer.
585      *
586      * To continue the same 112KB example, we have 62KB to slice up.
587      * We need 72 bytes * 100 bands = 7.2KB, leaving a 55K command buffer.
588      *
589      * A larger command buffer has some performance (see gxclmem.c comments)
590      * advantages in the general case, but is critical in one special case:
591      * high-level images. Whenever possible, images are transmitted across
592      * the band buffer in their original resolution and bits/pixel. The
593      * alternative fallback behavior can be very slow.  Here, the relevant
594      * restriction is that at least one entire source image row must fit
595      * into the command buffer. This means that, in our example, an RGB
596      * source image would have to be <= 18K pixels wide. If the image is
597      * sampled at the same resolution as the hardware (600 dpi), that means
598      * the row would be limited to a very reasonable 30 inches. However, if
599      * the source image is sampled at 2400 dpi, that limit is only 7.5
600      * inches. The situation gets worse as bands get smaller, but the
601      * implementor must decide on the tradeoff point.
602      *
603      * The moral of the story is that you should never make a band
604      * so small that its buffer limits the command buffer excessively.
605      * Again, Max image row bytes = band buffer size - # bands * 72.
606      *
607      * In the overlapped case, everything is exactly as above, except that
608      * two identical devices, each with an identical buffer, are allocated:
609      * one for the writer, and one for the rasterizer. Because it's critical
610      * to allocate identical buffers, I *strongly* recommend setting these
611      * params in the writer's open routine:
612      * space_params.band.BandBufferSpace, .BandWidth and .BandHeight.  If
613      * you don't force these values to a known value, the memory allocation
614      * heuristic may not come to the same result for both copies of the
615      * device, since the first allocation will diminish the amount of free
616      * memory.
617      *
618      * There is room for an important optimization here: allocate the
619      * writer's space with enough memory for a generous command buffer, but
620      * allocate the reader with only enough memory for a band rasterization
621      * buffer and the tile cache.  To do this, observe that the space_params
622      * struct has two sizes: BufferSpace vs. BandBufferSpace.  To start,
623      * BandBufferSpace is always <= BufferSpace. On the reader side,
624      * BandBufferSpace is divided between the tile cache and the rendering
625      * buffer -- that's all the memory that's needed to rasterize. On the
626      * writer's side, BandBufferSpace is divided the same way: the tile
627      * cache (which must be identical to the reader's) is carved out, and
628      * the space that would have been used for a rasterizing buffer is used
629      * as a command buffer. However, you can further increase the cmd buf
630      * further by setting BufferSize (not BandBufferSize) to a higher number
631      * than BandBufferSize. In that case, the command buffer is increased by
632      * the difference (BufferSize - BandBufferSize). There is logic in the
633      * memory allocation for printers that will automatically use BufferSize
634      * for writers (or non-async printers), and BandBufferSize for readers.
635      *
636      * Note: per the comments in gxclmem.c, the banding logic will perform
637      * better with 1MB or better for the command list.
638      */
639 
640     /* This will give us a very "ungenerous" buffer. */
641     /* Here, my arbitrary rule for min image row is: twice the dest width */
642     /* in full CMYK. */
643     int render_space;
644     int writer_space;
645     const int tile_cache_space = 50 * 1024;
646     const int min_image_rows = 2;
647     int min_row_space =
648 	min_image_rows * (  4 * ( pdev->width + sizeof(int) - 1 )  );
649     int min_band_height = max(1, pdev->height / 100);	/* make bands >= 1% of total */
650 
651     space_params->band.BandWidth = pdev->width;
652     space_params->band.BandHeight = min_band_height;
653 
654     render_space = gdev_mem_data_size( (const gx_device_memory *)pdev,
655 				       space_params->band.BandWidth,
656 				       space_params->band.BandHeight );
657     /* need to include minimal writer requirements to satisfy rasterizer init */
658     writer_space = 	/* add 5K slop for good measure */
659 	5000 + (72 + 8) * ( (pdev->height / space_params->band.BandHeight) + 1 );
660     space_params->band.BandBufferSpace =
661 	max(render_space, writer_space) + tile_cache_space;
662     space_params->BufferSpace =
663 	max(render_space, writer_space + min_row_space) + tile_cache_space;
664     /**************** HACK HACK HACK ****************/
665     /* Override this computation to force reader & writer to match */
666     space_params->BufferSpace = space_params->band.BandBufferSpace;
667 }
668 
669 /* Get device parameters. */
670 private int
bmpa_get_params(gx_device * pdev,gs_param_list * plist)671 bmpa_get_params(gx_device * pdev, gs_param_list * plist)
672 {
673     gx_device_async * const bdev = (gx_device_async *)pdev;
674 
675     return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
676 }
677 
678 /* Put device parameters. */
679 /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
680 /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
681 /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
682 /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/
683 private int
bmpa_put_params(gx_device * pdev,gs_param_list * plist)684 bmpa_put_params(gx_device *pdev, gs_param_list *plist)
685 {
686     /*
687      * This driver does nothing interesting except cascade down to
688      * gdev_prn_put_params_planar, which is something it would have to do
689      * even if it did do something interesting here.
690      *
691      * Note that gdev_prn_put_params[_planar] does not close the device.
692      */
693     gx_device_async * const bdev = (gx_device_async *)pdev;
694 
695     return gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer);
696 }
697 
698 /* Get hardware-detected parameters. */
699 /* This proc defines a only one param: a useless value for testing */
700 private int
bmpa_get_hardware_params(gx_device * dev,gs_param_list * plist)701 bmpa_get_hardware_params(gx_device *dev, gs_param_list *plist)
702 {
703     static const char *const test_value = "Test value";
704     static const char *const test_name = "TestValue";
705     int code = 0;
706 
707     if ( param_requested(plist, test_name) ) {
708 	gs_param_string param_str;
709 
710 	param_string_from_string(param_str, test_value); /* value must be persistent to use this macro */
711 	code = param_write_string(plist, test_name, &param_str);
712     }
713     return code;
714 }
715