/* Copyright (C) 1998, 1999, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevbmpa.c,v 1.3.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */ /* .BMP file format output drivers: Demo of ASYNC rendering */ /* 2000-04-20 ghost@aladdin.com - Makes device structures const, changing makefile entry from DEV to DEV2. */ /* 1998/12/29 ghost@aladdin.com - Modified to use gdev_prn_render_lines, which replaces the former "overlay" calls */ /* 1998/11/23 ghost@aladdin.com - Removed pointless restriction to single-page output */ /* 1998/7/28 ghost@aladdin.com - Factored out common BMP format code to gdevbmpc.c */ /* Initial version 2/2/98 by John Desrosiers (soho@crl.com) */ #include "stdio_.h" #include "gserrors.h" #include "gdevprna.h" #include "gdevpccm.h" #include "gdevbmp.h" #include "gdevppla.h" #include "gpsync.h" /* * The original version of this driver was restricted to producing a single * page per file. If for some reason you want to reinstate this * restriction, uncomment the next line. * NOTE: Even though the logic for multi-page files is straightforward, * it results in a file that most programs that process BMP format cannot * handle. Most programs will only display the first page. */ /*************** #define SINGLE_PAGE ****************/ /* ------ The device descriptors ------ */ /* Define data type for this device based on prn_device */ typedef struct gx_device_async_s { gx_device_common; gx_prn_device_common; bool UsePlanarBuffer; int buffered_page_exists; long file_offset_to_data[4]; } gx_device_async; /* Define initializer for device */ #define async_device(procs, dname, w10, h10, xdpi, ydpi, lm, bm, rm, tm, color_bits, print_page)\ { prn_device_std_margins_body(gx_device_async, procs, dname,\ w10, h10, xdpi, ydpi, lm, tm, lm, bm, rm, tm, color_bits, print_page),\ 0, 0, { 0, 0, 0, 0 }\ } private dev_proc_open_device(bmpa_writer_open); private dev_proc_open_device(bmpa_cmyk_writer_open); private prn_dev_proc_open_render_device(bmpa_reader_open_render_device); private dev_proc_print_page_copies(bmpa_reader_print_page_copies); /* VMS limits procedure names to 31 characters. */ private dev_proc_print_page_copies(bmpa_cmyk_reader_print_copies); private prn_dev_proc_buffer_page(bmpa_reader_buffer_page); private prn_dev_proc_buffer_page(bmpa_cmyk_reader_buffer_page); private dev_proc_output_page(bmpa_reader_output_page); private dev_proc_get_params(bmpa_get_params); private dev_proc_put_params(bmpa_put_params); private dev_proc_get_hardware_params(bmpa_get_hardware_params); private prn_dev_proc_start_render_thread(bmpa_reader_start_render_thread); private prn_dev_proc_get_space_params(bmpa_get_space_params); #define default_print_page 0 /* not needed becoz print_page_copies def'd */ /* Monochrome. */ private const gx_device_procs bmpamono_procs = prn_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close); const gx_device_async gs_bmpamono_device = async_device(bmpamono_procs, "bmpamono", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 1, default_print_page); /* 1-bit-per-plane separated CMYK color. */ #define bmpa_cmyk_procs(p_open, p_map_color_rgb, p_map_cmyk_color)\ p_open, NULL, NULL, gdev_prn_output_page, gdev_prn_close,\ NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\ bmpa_get_params, bmpa_put_params,\ p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device private const gx_device_procs bmpasep1_procs = { bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color) }; const gx_device_async gs_bmpasep1_device = { prn_device_body(gx_device_async, bmpasep1_procs, "bmpasep1", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 4, 4, 1, 1, 2, 2, default_print_page) }; /* 8-bit-per-plane separated CMYK color. */ private const gx_device_procs bmpasep8_procs = { bmpa_cmyk_procs(bmpa_cmyk_writer_open, cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color) }; const gx_device_async gs_bmpasep8_device = { prn_device_body(gx_device_async, bmpasep8_procs, "bmpasep8", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 4, 32, 255, 255, 256, 256, default_print_page) }; /* 4-bit (EGA/VGA-style) color. */ private const gx_device_procs bmpa16_procs = prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close, pc_4bit_map_rgb_color, pc_4bit_map_color_rgb); const gx_device_async gs_bmpa16_device = async_device(bmpa16_procs, "bmpa16", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 4, default_print_page); /* 8-bit (SuperVGA-style) color. */ /* (Uses a fixed palette of 3,3,2 bits.) */ private const gx_device_procs bmpa256_procs = prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close, pc_8bit_map_rgb_color, pc_8bit_map_color_rgb); const gx_device_async gs_bmpa256_device = async_device(bmpa256_procs, "bmpa256", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 8, default_print_page); /* 24-bit color. */ private const gx_device_procs bmpa16m_procs = prn_color_procs(bmpa_writer_open, gdev_prn_output_page, gdev_prn_close, bmp_map_16m_rgb_color, bmp_map_16m_color_rgb); const gx_device_async gs_bmpa16m_device = async_device(bmpa16m_procs, "bmpa16m", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0,0,0,0, /* margins */ 24, default_print_page); /* 32-bit CMYK color (outside the BMP specification). */ private const gx_device_procs bmpa32b_procs = { bmpa_cmyk_procs(bmpa_writer_open, gx_default_map_color_rgb, gx_default_cmyk_map_cmyk_color) }; const gx_device_async gs_bmpa32b_device = async_device(bmpa32b_procs, "bmpa32b", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0, 0, 0, 0, /* margins */ 32, default_print_page); /* --------- Forward declarations ---------- */ private void bmpa_reader_thread(P1(void *)); /* ------------ Writer Instance procedures ---------- */ /* Writer's open procedure */ private int bmpa_open_writer(gx_device *pdev /* Driver instance to open */, dev_proc_print_page_copies((*reader_print_page_copies)), prn_dev_proc_buffer_page((*reader_buffer_page))) { gx_device_async * const pwdev = (gx_device_async *)pdev; int max_width; int max_raster; int min_band_height; int max_src_image_row; /* * Set up device's printer proc vector to point to this driver, since * there are no convenient macros for setting them up in static template. */ init_async_render_procs(pwdev, bmpa_reader_start_render_thread, reader_buffer_page, reader_print_page_copies); set_dev_proc(pdev, get_params, bmpa_get_params); /* because not all device-init macros allow this to be defined */ set_dev_proc(pdev, put_params, bmpa_put_params); /* ibid. */ set_dev_proc(pdev, get_hardware_params, bmpa_get_hardware_params); set_dev_proc(pdev, output_page, bmpa_reader_output_page); /* hack */ pwdev->printer_procs.get_space_params = bmpa_get_space_params; pwdev->printer_procs.open_render_device = bmpa_reader_open_render_device; /* Included for tutorial value */ /* * Determine MAXIMUM parameters this device will have to support over * lifetime. See comments for bmpa_get_space_params(). */ max_width = DEFAULT_WIDTH_10THS * 60; /* figure max wid = default @ 600dpi */ min_band_height = max(1, (DEFAULT_HEIGHT_10THS * 60) / 100); max_raster = bitmap_raster(max_width * pwdev->color_info.depth); /* doesn't need to be super accurate */ max_src_image_row = max_width * 4 * 2; /* Set to planar buffering mode if appropriate. */ if (pwdev->UsePlanarBuffer) gdev_prn_set_procs_planar(pdev); /* Special writer open routine for async interpretation */ /* Starts render thread */ return gdev_prn_async_write_open((gx_device_printer *)pdev, max_raster, min_band_height, max_src_image_row); } private int bmpa_writer_open(gx_device *pdev /* Driver instance to open */) { return bmpa_open_writer(pdev, bmpa_reader_print_page_copies, bmpa_reader_buffer_page); } private int bmpa_cmyk_writer_open(gx_device *pdev /* Driver instance to open */) { return bmpa_open_writer(pdev, bmpa_cmyk_reader_print_copies, bmpa_cmyk_reader_buffer_page); } /* -------------- Renderer instance procedures ----------*/ /* Forward declarations */ private int bmpa_reader_buffer_planes(P6(gx_device_printer *pdev, FILE *prn_stream, int num_copies, int first_plane, int last_plane, int raster)); /* Thread to do rendering, started by bmpa_reader_start_render_thread */ private void bmpa_reader_thread(void *params) { gdev_prn_async_render_thread((gdev_prn_start_render_params *)params); } private int /* rets 0 ok, -ve error if couldn't start thread */ bmpa_reader_start_render_thread(gdev_prn_start_render_params *params) { return gp_create_thread(bmpa_reader_thread, params); } private int bmpa_reader_open_render_device(gx_device_printer *ppdev) { /* * Do anything that needs to be done at open time here. * Since this implementation doesn't do anything, we don't need to * cast the device argument to the more specific type. */ /*gx_device_async * const prdev = (gx_device_async *)ppdev;*/ /* Cascade down to the default handler */ return gdev_prn_async_render_open(ppdev); } /* Generic routine to send the page to the printer. */ private int bmpa_reader_output_page(gx_device *pdev, int num_copies, int flush) { /* * HACK: open the printer page with the positionable attribute since * we need to seek back & forth to support partial rendering. */ if ( num_copies > 0 || !flush ) { int code = gdev_prn_open_printer_positionable(pdev, 1, 1); if ( code < 0 ) return code; } return gdev_prn_output_page(pdev, num_copies, flush); } private int bmpa_reader_print_planes(gx_device_printer *pdev, FILE *prn_stream, int num_copies, int first_plane, int last_plane, int raster) { gx_device_async * const prdev = (gx_device_async *)pdev; /* BMP scan lines are padded to 32 bits. */ uint bmp_raster = raster + (-raster & 3); int code = 0; int y; byte *row = 0; byte *raster_data; int plane; /* If there's data in buffer, need to process w/overlays */ if (prdev->buffered_page_exists) { code = bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, first_plane, last_plane, raster); goto done; } #ifdef SINGLE_PAGE /* BMP format is single page, so discard all but 1st printable page */ /* Since the OutputFile may have a %d, we use ftell to determine if */ /* this is a zero length file, which is legal to write */ if (ftell(prn_stream) != 0) return 0; #endif row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer"); if (row == 0) /* can't allocate row buffer */ return_error(gs_error_VMerror); for (plane = first_plane; plane <= last_plane; ++plane) { gx_render_plane_t render_plane; /* Write header & seek to its end */ code = (first_plane < 0 ? write_bmp_header(pdev, prn_stream) : write_bmp_separated_header(pdev, prn_stream)); if (code < 0) goto done; /* Save the file offset where data begins */ if ((prdev->file_offset_to_data[plane - first_plane] = ftell(prn_stream)) == -1L) { code = gs_note_error(gs_error_ioerror); goto done; } /* * Write out the bands top to bottom. Finish the job even if * num_copies == 0, to avoid invalid output file. */ if (plane >= 0) gx_render_plane_init(&render_plane, (gx_device *)pdev, plane); for (y = prdev->height - 1; y >= 0; y--) { uint actual_raster; code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster, &raster_data, &actual_raster, (plane < 0 ? NULL : &render_plane)); if (code < 0) goto done; if (fwrite((const char *)raster_data, actual_raster, 1, prn_stream) < 1) { code = gs_error_ioerror; goto done; } } } done: gs_free_object(pdev->memory, row, "bmp file buffer"); prdev->buffered_page_exists = 0; return code; } private int bmpa_reader_print_page_copies(gx_device_printer *pdev, FILE *prn_stream, int num_copies) { return bmpa_reader_print_planes(pdev, prn_stream, num_copies, -1, -1, gdev_prn_raster(pdev)); } private int bmpa_cmyk_plane_raster(gx_device_printer *pdev) { return bitmap_raster(pdev->width * (pdev->color_info.depth / 4)); } private int bmpa_cmyk_reader_print_copies(gx_device_printer *pdev, FILE *prn_stream, int num_copies) { return bmpa_reader_print_planes(pdev, prn_stream, num_copies, 0, 3, bmpa_cmyk_plane_raster(pdev)); } /* Buffer a (partial) rasterized page & optionally print result multiple times. */ private int bmpa_reader_buffer_planes(gx_device_printer *pdev, FILE *file, int num_copies, int first_plane, int last_plane, int raster) { gx_device_async * const prdev = (gx_device_async *)pdev; gx_device * const dev = (gx_device *)pdev; int code = 0; /* If there's no data in buffer, no need to do any overlays */ if (!prdev->buffered_page_exists) { code = bmpa_reader_print_planes(pdev, file, num_copies, first_plane, last_plane, raster); goto done; } /* * Continue rendering on top of the existing file. This requires setting * up a buffer of the existing bits in GS's format (except for optional * extra padding bytes at the end of each scan line, provided the scan * lines are still correctly memory-aligned) and then calling * gdev_prn_render_lines. If the device already provides a band buffer * -- which currently is always the case -- we can use it if we want; * but if a device stores partially rendered pages in memory in a * compatible format (e.g., a printer with a hardware page buffer), it * can render directly on top of the stored bits. * * If we can render exactly one band (or N bands) at a time, this is * more efficient, since otherwise (a) band(s) will have to be rendered * more than once. */ { byte *raster_data; gx_device_clist_reader *const crdev = (gx_device_clist_reader *)pdev; int raster = gx_device_raster(dev, 1); int padding = -raster & 3; /* BMP scan lines are padded to 32 bits. */ int bmp_raster = raster + padding; int plane; /* * Get the address of the renderer's band buffer. In the future, * it will be possible to suppress the allocation of this buffer, * and to use only buffers provided the driver itself (e.g., a * hardware buffer). */ if (!pdev->buffer_space) { /* Not banding. Can't happen. */ code = gs_note_error(gs_error_Fatal); goto done; } raster_data = crdev->data; for (plane = first_plane; plane <= last_plane; ++plane) { gx_render_plane_t render_plane; gx_device *bdev; int y, band_base_line; /* Seek to beginning of data portion of file */ if (fseek(file, prdev->file_offset_to_data[plane - first_plane], SEEK_SET)) { code = gs_note_error(gs_error_ioerror); goto done; } if (plane >= 0) gx_render_plane_init(&render_plane, (gx_device *)pdev, plane); else render_plane.index = -1; /* Set up the buffer device. */ code = gdev_create_buf_device(crdev->buf_procs.create_buf_device, &bdev, crdev->target, &render_plane, dev->memory, true); if (code < 0) goto done; /* * Iterate thru bands from top to bottom. As noted above, we * do this an entire band at a time for efficiency. */ for (y = dev->height - 1; y >= 0; y = band_base_line - 1) { int band_height = dev_proc(dev, get_band)(dev, y, &band_base_line); int line; gs_int_rect band_rect; /* Set up the buffer device for this band. */ code = crdev->buf_procs.setup_buf_device (bdev, raster_data, bmp_raster, NULL, 0, band_height, band_height); if (code < 0) goto done; /* Fill in the buffer with a band from the BMP file. */ /* Need to do this backward since BMP is top to bottom. */ for (line = band_height - 1; line >= 0; --line) if (fread(raster_data + line * bmp_raster, raster, 1, file) < 1 || fseek(file, padding, SEEK_CUR) ) { code = gs_note_error(gs_error_ioerror); goto done; } /* Continue rendering on top of the existing bits. */ band_rect.p.x = 0; band_rect.p.y = band_base_line; band_rect.q.x = pdev->width; band_rect.q.y = band_base_line + band_height; if ((code = clist_render_rectangle((gx_device_clist *)pdev, &band_rect, bdev, &render_plane, false)) < 0) goto done; /* Rewind & write out the updated buffer. */ if (fseek(file, -bmp_raster * band_height, SEEK_CUR)) { code = gs_note_error(gs_error_ioerror); goto done; } for (line = band_height - 1; line >= 0; --line) { if (fwrite(raster_data + line * bmp_raster, bmp_raster, 1, file) < 1 || fseek(file, padding, SEEK_CUR) ) { code = gs_note_error(gs_error_ioerror); goto done; } } } crdev->buf_procs.destroy_buf_device(bdev); } } done: prdev->buffered_page_exists = (code >= 0); return code; } private int bmpa_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream, int num_copies) { return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, -1, -1, gdev_prn_raster(pdev)); } private int bmpa_cmyk_reader_buffer_page(gx_device_printer *pdev, FILE *prn_stream, int num_copies) { return bmpa_reader_buffer_planes(pdev, prn_stream, num_copies, 0, 3, bmpa_cmyk_plane_raster(pdev)); } /*------------ Procedures common to writer & renderer -------- */ /* Compute space parameters */ private void bmpa_get_space_params(const gx_device_printer *pdev, gdev_prn_space_params *space_params) { /* Plug params into device before opening it * * You ask "How did you come up with these #'s?" You asked, so... * * To answer clearly, let me begin by recapitulating how command list * (clist) device memory allocation works in the non-overlapped case: * When the device is opened, a buffer is allocated. How big? For * starters, it must be >= PRN_MIN_BUFFER_SPACE, and as we'll see, must * be sufficient to satisfy the rest of the band params. If you don't * specify a size for it in space_params.band.BandBufferSpace, the open * routine will use a heuristic where it tries to use PRN_BUFFER_SPACE, * then works its way down by factors of 2 if that much memory isn't * available. * * The device proceeds to divide the buffer into several parts: one of * them is used for the same thing during writing & rasterizing; the * other parts are redivided and used differently writing and * rasterizing. The limiting factor dictating memory requirements is the * rasterizer's render buffer. This buffer needs to be able to contain * a pixmap that covers an entire band. Memory consumption is whatever * is needed to hold N rows of data aligned on word boundaries, + * sizeof(pointer) for each of N rows. Whatever is left over in the * rasterized is allocated to a tile cache. You want to make sure that * cache is at least 50KB. * * For example, take a 600 dpi b/w device at 8.5 x 11 inches. For the * whole device, that's 6600 rows @ 638 bytes = ~4.2 MB total. If the * device is divided into 100 bands, each band's rasterizer buffer is * 62K. Add on a 50K tile cache, and you get a 112KB (+ add a little * slop) total device buffer size. * * Now that we've covered the rasterizer, let's switch back to the * writer. The writer must have a tile cache *exactly* the same size as * the reader. This means that the space to divide up for the writer is * equal is size to the rasterizer's band buffer. This space is divided * into 2 sections: per-band bookeeping info and a command buffer. The * bookeeping info currently uses ~72 bytes for each band. The rest is * the command buffer. * * To continue the same 112KB example, we have 62KB to slice up. * We need 72 bytes * 100 bands = 7.2KB, leaving a 55K command buffer. * * A larger command buffer has some performance (see gxclmem.c comments) * advantages in the general case, but is critical in one special case: * high-level images. Whenever possible, images are transmitted across * the band buffer in their original resolution and bits/pixel. The * alternative fallback behavior can be very slow. Here, the relevant * restriction is that at least one entire source image row must fit * into the command buffer. This means that, in our example, an RGB * source image would have to be <= 18K pixels wide. If the image is * sampled at the same resolution as the hardware (600 dpi), that means * the row would be limited to a very reasonable 30 inches. However, if * the source image is sampled at 2400 dpi, that limit is only 7.5 * inches. The situation gets worse as bands get smaller, but the * implementor must decide on the tradeoff point. * * The moral of the story is that you should never make a band * so small that its buffer limits the command buffer excessively. * Again, Max image row bytes = band buffer size - # bands * 72. * * In the overlapped case, everything is exactly as above, except that * two identical devices, each with an identical buffer, are allocated: * one for the writer, and one for the rasterizer. Because it's critical * to allocate identical buffers, I *strongly* recommend setting these * params in the writer's open routine: * space_params.band.BandBufferSpace, .BandWidth and .BandHeight. If * you don't force these values to a known value, the memory allocation * heuristic may not come to the same result for both copies of the * device, since the first allocation will diminish the amount of free * memory. * * There is room for an important optimization here: allocate the * writer's space with enough memory for a generous command buffer, but * allocate the reader with only enough memory for a band rasterization * buffer and the tile cache. To do this, observe that the space_params * struct has two sizes: BufferSpace vs. BandBufferSpace. To start, * BandBufferSpace is always <= BufferSpace. On the reader side, * BandBufferSpace is divided between the tile cache and the rendering * buffer -- that's all the memory that's needed to rasterize. On the * writer's side, BandBufferSpace is divided the same way: the tile * cache (which must be identical to the reader's) is carved out, and * the space that would have been used for a rasterizing buffer is used * as a command buffer. However, you can further increase the cmd buf * further by setting BufferSize (not BandBufferSize) to a higher number * than BandBufferSize. In that case, the command buffer is increased by * the difference (BufferSize - BandBufferSize). There is logic in the * memory allocation for printers that will automatically use BufferSize * for writers (or non-async printers), and BandBufferSize for readers. * * Note: per the comments in gxclmem.c, the banding logic will perform * better with 1MB or better for the command list. */ /* This will give us a very "ungenerous" buffer. */ /* Here, my arbitrary rule for min image row is: twice the dest width */ /* in full CMYK. */ int render_space; int writer_space; const int tile_cache_space = 50 * 1024; const int min_image_rows = 2; int min_row_space = min_image_rows * ( 4 * ( pdev->width + sizeof(int) - 1 ) ); int min_band_height = max(1, pdev->height / 100); /* make bands >= 1% of total */ space_params->band.BandWidth = pdev->width; space_params->band.BandHeight = min_band_height; render_space = gdev_mem_data_size( (const gx_device_memory *)pdev, space_params->band.BandWidth, space_params->band.BandHeight ); /* need to include minimal writer requirements to satisfy rasterizer init */ writer_space = /* add 5K slop for good measure */ 5000 + (72 + 8) * ( (pdev->height / space_params->band.BandHeight) + 1 ); space_params->band.BandBufferSpace = max(render_space, writer_space) + tile_cache_space; space_params->BufferSpace = max(render_space, writer_space + min_row_space) + tile_cache_space; /**************** HACK HACK HACK ****************/ /* Override this computation to force reader & writer to match */ space_params->BufferSpace = space_params->band.BandBufferSpace; } /* Get device parameters. */ private int bmpa_get_params(gx_device * pdev, gs_param_list * plist) { gx_device_async * const bdev = (gx_device_async *)pdev; return gdev_prn_get_params_planar(pdev, plist, &bdev->UsePlanarBuffer); } /* Put device parameters. */ /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/ /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/ /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/ /* IMPORTANT: async drivers must NOT CLOSE the device while doing put_params.*/ private int bmpa_put_params(gx_device *pdev, gs_param_list *plist) { /* * This driver does nothing interesting except cascade down to * gdev_prn_put_params_planar, which is something it would have to do * even if it did do something interesting here. * * Note that gdev_prn_put_params[_planar] does not close the device. */ gx_device_async * const bdev = (gx_device_async *)pdev; return gdev_prn_put_params_planar(pdev, plist, &bdev->UsePlanarBuffer); } /* Get hardware-detected parameters. */ /* This proc defines a only one param: a useless value for testing */ private int bmpa_get_hardware_params(gx_device *dev, gs_param_list *plist) { static const char *const test_value = "Test value"; static const char *const test_name = "TestValue"; int code = 0; if ( param_requested(plist, test_name) ) { gs_param_string param_str; param_string_from_string(param_str, test_value); /* value must be persistent to use this macro */ code = param_write_string(plist, test_name, ¶m_str); } return code; }