/* Copyright (C) 1991, 1996, 1997, 1998, 1999 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: gxclread.c,v 1.3.4.1.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Command list reading for Ghostscript. */ #include "memory_.h" #include "gx.h" #include "gp.h" /* for gp_fmode_rb */ #include "gpcheck.h" #include "gserrors.h" #include "gxdevice.h" #include "gscoord.h" /* requires gsmatrix.h */ #include "gsdevice.h" /* for gs_deviceinitialmatrix */ #include "gxdevmem.h" /* must precede gxcldev.h */ #include "gxcldev.h" #include "gxgetbit.h" #include "gxhttile.h" #include "gdevht.h" #include "gdevplnx.h" /* * We really don't like the fact that gdevprn.h is included here, since * command lists are supposed to be usable for purposes other than printer * devices; but gdev_prn_colors_used and gdev_create_buf_device are * currently only applicable to printer devices. */ #include "gdevprn.h" #include "stream.h" #include "strimpl.h" /* ------ Band file reading stream ------ */ /* * To separate banding per se from command list interpretation, * we make the command list interpreter simply read from a stream. * When we are actually doing banding, the stream filters the band file * and only passes through the commands for the current bands (or band * ranges that include a current band). */ typedef struct stream_band_read_state_s { stream_state_common; gx_band_page_info_t page_info; int band_first, band_last; uint left; /* amount of data left in this run */ cmd_block b_this; } stream_band_read_state; private int s_band_read_init(stream_state * st) { stream_band_read_state *const ss = (stream_band_read_state *) st; ss->left = 0; ss->b_this.band_min = 0; ss->b_this.band_max = 0; ss->b_this.pos = 0; clist_rewind(ss->page_bfile, false, ss->page_bfname); return 0; } private int s_band_read_process(stream_state * st, stream_cursor_read * ignore_pr, stream_cursor_write * pw, bool last) { stream_band_read_state *const ss = (stream_band_read_state *) st; register byte *q = pw->ptr; byte *wlimit = pw->limit; clist_file_ptr cfile = ss->page_cfile; clist_file_ptr bfile = ss->page_bfile; uint left = ss->left; int status = 1; uint count; while ((count = wlimit - q) != 0) { if (left) { /* Read more data for the current run. */ if (count > left) count = left; clist_fread_chars(q + 1, count, cfile); if (clist_ferror_code(cfile) < 0) { status = ERRC; break; } q += count; left -= count; process_interrupts(); continue; } rb: /* * Scan for the next run for the current bands (or a band range * that includes a current band). */ if (ss->b_this.band_min == cmd_band_end && clist_ftell(bfile) == ss->page_bfile_end_pos ) { status = EOFC; break; } { int bmin = ss->b_this.band_min; int bmax = ss->b_this.band_max; long pos = ss->b_this.pos; clist_fread_chars(&ss->b_this, sizeof(ss->b_this), bfile); if (!(ss->band_last >= bmin && ss->band_first <= bmax)) goto rb; clist_fseek(cfile, pos, SEEK_SET, ss->page_cfname); left = (uint) (ss->b_this.pos - pos); if_debug5('l', "[l]reading for bands (%d,%d) at bfile %ld, cfile %ld, length %u\n", bmin, bmax, clist_ftell(bfile) - 2 * sizeof(ss->b_this), pos, left); } } pw->ptr = q; ss->left = left; return status; } /* Stream template */ private const stream_template s_band_read_template = { &st_stream_state, s_band_read_init, s_band_read_process, 1, cbuf_size }; /* ------ Reading/rendering ------ */ /* Forward references */ private int clist_render_init(P1(gx_device_clist *)); private int clist_playback_file_bands(P8(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)); private int clist_rasterize_lines(P6(gx_device *dev, int y, int lineCount, gx_device *bdev, const gx_render_plane_t *render_plane, int *pmy)); /* Calculate the raster for a chunky or planar device. */ private int clist_plane_raster(const gx_device *dev, const gx_render_plane_t *render_plane) { return bitmap_raster(dev->width * (render_plane && render_plane->index >= 0 ? render_plane->depth : dev->color_info.depth)); } /* Select full-pixel rendering if required for RasterOp. */ private void clist_select_render_plane(gx_device *dev, int y, int height, gx_render_plane_t *render_plane, int index) { if (index >= 0) { gx_colors_used_t colors_used; int ignore_start; gdev_prn_colors_used(dev, y, height, &colors_used, &ignore_start); if (colors_used.slow_rop) index = -1; } if (index < 0) render_plane->index = index; else gx_render_plane_init(render_plane, dev, index); } /* * Do device setup from params stored in command list. This is only for * async rendering & assumes that the first command in every command list * is a put_params command which sets all space-related parameters to the * value they will have for the duration of that command list. */ int clist_setup_params(gx_device *dev) { gx_device_clist_reader * const crdev = &((gx_device_clist *)dev)->reader; int code = clist_render_init((gx_device_clist *)dev); if (code < 0) return code; code = clist_playback_file_bands(playback_action_setup, crdev, &crdev->page_info, 0, 0, 0, 0, 0); /* put_params may have reinitialized device into a writer */ clist_render_init((gx_device_clist *)dev); return code; } /* Copy a rasterized rectangle to the client, rasterizing if needed. */ int clist_get_bits_rectangle(gx_device *dev, const gs_int_rect * prect, gs_get_bits_params_t *params, gs_int_rect **unread) { gx_device_clist_common *cdev = (gx_device_clist_common *)dev; gs_get_bits_options_t options = params->options; int y = prect->p.y; int end_y = prect->q.y; int line_count = end_y - y; gs_int_rect band_rect; int lines_rasterized; gx_device *bdev; int num_planes = (options & GB_PACKING_CHUNKY ? 1 : options & GB_PACKING_PLANAR ? dev->color_info.num_components : options & GB_PACKING_BIT_PLANAR ? dev->color_info.depth : 0 /****** NOT POSSIBLE ******/); gx_render_plane_t render_plane; int plane_index; int my; int code; if (prect->p.x < 0 || prect->q.x > dev->width || y < 0 || end_y > dev->height ) return_error(gs_error_rangecheck); if (line_count <= 0 || prect->p.x >= prect->q.x) return 0; /* * Calculate the render_plane from the params. There are two cases: * full pixels, or a single plane. */ plane_index = -1; if (options & GB_SELECT_PLANES) { /* Look for the one selected plane. */ int i; for (i = 0; i < num_planes; ++i) if (params->data[i]) { if (plane_index >= 0) /* >1 plane requested */ return gx_default_get_bits_rectangle(dev, prect, params, unread); plane_index = i; } } clist_select_render_plane(dev, y, line_count, &render_plane, plane_index); code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &bdev, cdev->target, &render_plane, dev->memory, true); if (code < 0) return code; code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my); if (code < 0) return code; lines_rasterized = min(code, line_count); /* Return as much of the rectangle as falls within the rasterized lines. */ band_rect = *prect; band_rect.p.y = my; band_rect.q.y = my + lines_rasterized; code = dev_proc(bdev, get_bits_rectangle) (bdev, &band_rect, params, unread); cdev->buf_procs.destroy_buf_device(bdev); if (code < 0 || lines_rasterized == line_count) return code; /* * We'll have to return the rectangle in pieces. Force GB_RETURN_COPY * rather than GB_RETURN_POINTER, and require all subsequent pieces to * use the same values as the first piece for all of the other format * options. If copying isn't allowed, or if there are any unread * rectangles, punt. */ if (!(options & GB_RETURN_COPY) || code > 0) return gx_default_get_bits_rectangle(dev, prect, params, unread); options = params->options; if (!(options & GB_RETURN_COPY)) { /* Redo the first piece with copying. */ params->options = options = (params->options & ~GB_RETURN_ALL) | GB_RETURN_COPY; lines_rasterized = 0; } { gs_get_bits_params_t band_params; uint raster = gx_device_raster(bdev, true); code = gdev_create_buf_device(cdev->buf_procs.create_buf_device, &bdev, cdev->target, &render_plane, dev->memory, true); if (code < 0) return code; band_params = *params; while ((y += lines_rasterized) < end_y) { int i; /* Increment data pointers by lines_rasterized. */ for (i = 0; i < num_planes; ++i) if (band_params.data[i]) band_params.data[i] += raster * lines_rasterized; line_count = end_y - y; code = clist_rasterize_lines(dev, y, line_count, bdev, &render_plane, &my); if (code < 0) break; lines_rasterized = min(code, line_count); band_rect.p.y = my; band_rect.q.y = my + lines_rasterized; code = dev_proc(bdev, get_bits_rectangle) (bdev, &band_rect, &band_params, unread); if (code < 0) break; params->options = options = band_params.options; if (lines_rasterized == line_count) break; } cdev->buf_procs.destroy_buf_device(bdev); } return code; } /* Copy scan lines to the client. This is where rendering gets done. */ /* Processes min(requested # lines, # lines available thru end of band) */ 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) { gx_device_clist * const cdev = (gx_device_clist *)dev; gx_device_clist_reader * const crdev = &cdev->reader; gx_device *target = crdev->target; uint raster = clist_plane_raster(target, render_plane); byte *mdata = crdev->data + crdev->page_tile_cache_size; int plane_index = (render_plane ? render_plane->index : -1); int code; /* Render a band if necessary, and copy it incrementally. */ if (crdev->ymin < 0 || crdev->yplane.index != plane_index || !(y >= crdev->ymin && y < crdev->ymax) ) { int band_height = crdev->page_band_height; int band = y / band_height; int band_begin_line = band * band_height; int band_end_line = band_begin_line + band_height; int band_num_lines; gs_int_rect band_rect; if (band_end_line > dev->height) band_end_line = dev->height; /* Clip line_count to current band */ if (line_count > band_end_line - y) line_count = band_end_line - y; band_num_lines = band_end_line - band_begin_line; if (y < 0 || y > dev->height) return_error(gs_error_rangecheck); code = crdev->buf_procs.setup_buf_device (bdev, mdata, raster, NULL, 0, band_num_lines, band_num_lines); band_rect.p.x = 0; band_rect.p.y = band_begin_line; band_rect.q.x = dev->width; band_rect.q.y = band_end_line; if (code >= 0) code = clist_render_rectangle(cdev, &band_rect, bdev, render_plane, true); /* Reset the band boundaries now, so that we don't get */ /* an infinite loop. */ crdev->ymin = band_begin_line; crdev->ymax = band_end_line; if (code < 0) return code; } if (line_count > crdev->ymax - y) line_count = crdev->ymax - y; code = crdev->buf_procs.setup_buf_device (bdev, mdata, raster, NULL, y - crdev->ymin, line_count, crdev->ymax - crdev->ymin); if (code < 0) return code; *pmy = 0; return line_count; } /* Initialize for reading. */ private int clist_render_init(gx_device_clist *dev) { gx_device_clist_reader * const crdev = &dev->reader; crdev->ymin = crdev->ymax = 0; crdev->yplane.index = -1; /* For normal rasterizing, pages and num_pages are zero. */ crdev->pages = 0; crdev->num_pages = 0; return 0; } /* * Render a rectangle to a client-supplied device. There is no necessary * relationship between band boundaries and the region being rendered. */ 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) { gx_device_clist_reader * const crdev = &cdev->reader; const gx_placed_page *ppages; int num_pages = crdev->num_pages; int band_height = crdev->page_band_height; int band_first = prect->p.y / band_height; int band_last = (prect->q.y - 1) / band_height; gx_saved_page current_page; gx_placed_page placed_page; int code = 0; int i; /* Initialize for rendering if we haven't done so yet. */ if (crdev->ymin < 0) { code = clist_end_page(&cdev->writer); if (code < 0) return code; code = clist_render_init(cdev); if (code < 0) return code; } if (render_plane) crdev->yplane = *render_plane; else crdev->yplane.index = -1; if_debug2('l', "[l]rendering bands (%d,%d)\n", band_first, band_last); if (clear) dev_proc(bdev, fill_rectangle) (bdev, 0, 0, bdev->width, bdev->height, gx_device_white(bdev)); /* * If we aren't rendering saved pages, do the current one. * Note that this is the only case in which we may encounter * a gx_saved_page with non-zero cfile or bfile. */ ppages = crdev->pages; if (ppages == 0) { current_page.info = crdev->page_info; placed_page.page = ¤t_page; placed_page.offset.x = placed_page.offset.y = 0; ppages = &placed_page; num_pages = 1; } for (i = 0; i < num_pages && code >= 0; ++i) { const gx_placed_page *ppage = &ppages[i]; code = clist_playback_file_bands(playback_action_render, crdev, &ppage->page->info, bdev, band_first, band_last, prect->p.x - ppage->offset.x, prect->p.y); } return code; } /* Playback the band file, taking the indicated action w/ its contents. */ 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) { int code = 0; bool opened_bfile = false; bool opened_cfile = false; /* We have to pick some allocator for rendering.... */ gs_memory_t *mem = (cdev->memory != 0 ? cdev->memory : &gs_memory_default); stream_band_read_state rs; /* setup stream */ s_init_state((stream_state *)&rs, &s_band_read_template, (gs_memory_t *)0); rs.band_first = band_first; rs.band_last = band_last; rs.page_info = *page_info; /* If this is a saved page, open the files. */ if (rs.page_cfile == 0) { code = clist_fopen(rs.page_cfname, gp_fmode_rb, &rs.page_cfile, cdev->bandlist_memory, cdev->bandlist_memory, true); opened_cfile = (code >= 0); } if (rs.page_bfile == 0 && code >= 0) { code = clist_fopen(rs.page_bfname, gp_fmode_rb, &rs.page_bfile, cdev->bandlist_memory, cdev->bandlist_memory, false); opened_bfile = (code >= 0); } if (rs.page_cfile != 0 && rs.page_bfile != 0) { stream s; byte sbuf[cbuf_size]; static const stream_procs no_procs = { s_std_noavailable, s_std_noseek, s_std_read_reset, s_std_read_flush, s_std_close, s_band_read_process }; s_band_read_init((stream_state *)&rs); s_std_init(&s, sbuf, cbuf_size, &no_procs, s_mode_read); s.foreign = 1; s.state = (stream_state *)&rs; code = clist_playback_band(action, cdev, &s, target, x0, y0, mem); } /* Close the files if we just opened them. */ if (opened_bfile && rs.page_bfile != 0) clist_fclose(rs.page_bfile, rs.page_bfname, false); if (opened_cfile && rs.page_cfile != 0) clist_fclose(rs.page_cfile, rs.page_cfname, false); return code; }