/* Copyright (C) 1998 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: gxclutil.c,v 1.5.2.1.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Command list writing utilities. */ #include "memory_.h" #include "string_.h" #include "gx.h" #include "gp.h" #include "gpcheck.h" #include "gserrors.h" #include "gxdevice.h" #include "gxdevmem.h" /* must precede gxcldev.h */ #include "gxcldev.h" #include "gxclpath.h" #include "gsparams.h" /* ---------------- Statistics ---------------- */ #ifdef DEBUG const char *const cmd_op_names[16] = {cmd_op_name_strings}; private const char *const cmd_misc_op_names[16] = {cmd_misc_op_name_strings}; private const char *const cmd_misc2_op_names[16] = {cmd_misc2_op_name_strings}; private const char *const cmd_segment_op_names[16] = {cmd_segment_op_name_strings}; private const char *const cmd_path_op_names[16] = {cmd_path_op_name_strings}; const char *const *const cmd_sub_op_names[16] = {cmd_misc_op_names, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, cmd_misc2_op_names, cmd_segment_op_names, cmd_path_op_names }; struct stats_cmd_s { ulong op_counts[256]; ulong op_sizes[256]; ulong tile_reset, tile_found, tile_added; ulong same_band, other_band; } stats_cmd; extern ulong stats_cmd_diffs[5]; /* in gxclpath.c */ int cmd_count_op(int op, uint size) { stats_cmd.op_counts[op]++; stats_cmd.op_sizes[op] += size; if (gs_debug_c('L')) { const char *const *sub = cmd_sub_op_names[op >> 4]; if (sub) dlprintf2(", %s(%u)\n", sub[op & 0xf], size); else dlprintf3(", %s %d(%u)\n", cmd_op_names[op >> 4], op & 0xf, size); dflush(); } return op; } void cmd_uncount_op(int op, uint size) { stats_cmd.op_counts[op]--; stats_cmd.op_sizes[op] -= size; } #endif /* Print statistics. */ #ifdef DEBUG void cmd_print_stats(void) { int ci, cj; dlprintf3("[l]counts: reset = %lu, found = %lu, added = %lu\n", stats_cmd.tile_reset, stats_cmd.tile_found, stats_cmd.tile_added); dlprintf5(" diff 2.5 = %lu, 3 = %lu, 4 = %lu, 2 = %lu, >4 = %lu\n", stats_cmd_diffs[0], stats_cmd_diffs[1], stats_cmd_diffs[2], stats_cmd_diffs[3], stats_cmd_diffs[4]); dlprintf2(" same_band = %lu, other_band = %lu\n", stats_cmd.same_band, stats_cmd.other_band); for (ci = 0; ci < 0x100; ci += 0x10) { const char *const *sub = cmd_sub_op_names[ci >> 4]; if (sub != 0) { dlprintf1("[l] %s =", cmd_op_names[ci >> 4]); for (cj = ci; cj < ci + 0x10; cj += 2) dprintf6("\n\t%s = %lu(%lu), %s = %lu(%lu)", sub[cj - ci], stats_cmd.op_counts[cj], stats_cmd.op_sizes[cj], sub[cj - ci + 1], stats_cmd.op_counts[cj + 1], stats_cmd.op_sizes[cj + 1]); } else { ulong tcounts = 0, tsizes = 0; for (cj = ci; cj < ci + 0x10; cj++) tcounts += stats_cmd.op_counts[cj], tsizes += stats_cmd.op_sizes[cj]; dlprintf3("[l] %s (%lu,%lu) =\n\t", cmd_op_names[ci >> 4], tcounts, tsizes); for (cj = ci; cj < ci + 0x10; cj++) if (stats_cmd.op_counts[cj] == 0) dputs(" -"); else dprintf2(" %lu(%lu)", stats_cmd.op_counts[cj], stats_cmd.op_sizes[cj]); } dputs("\n"); } } #endif /* DEBUG */ /* ---------------- Writing utilities ---------------- */ /* Write the commands for one band or band range. */ private int /* ret 0 all ok, -ve error code, or +1 ok w/low-mem warning */ cmd_write_band(gx_device_clist_writer * cldev, int band_min, int band_max, cmd_list * pcl, byte cmd_end) { const cmd_prefix *cp = pcl->head; int code_b = 0; int code_c = 0; if (cp != 0 || cmd_end != cmd_opv_end_run) { clist_file_ptr cfile = cldev->page_cfile; clist_file_ptr bfile = cldev->page_bfile; cmd_block cb; byte end = cmd_count_op(cmd_end, 1); if (cfile == 0 || bfile == 0) return_error(gs_error_ioerror); cb.band_min = band_min; cb.band_max = band_max; cb.pos = clist_ftell(cfile); if_debug3('l', "[l]writing for bands (%d,%d) at %ld\n", band_min, band_max, cb.pos); clist_fwrite_chars(&cb, sizeof(cb), bfile); if (cp != 0) { pcl->tail->next = 0; /* terminate the list */ for (; cp != 0; cp = cp->next) { #ifdef DEBUG if ((const byte *)cp < cldev->cbuf || (const byte *)cp >= cldev->cend || cp->size > cldev->cend - (const byte *)cp ) { lprintf1("cmd_write_band error at 0x%lx\n", (ulong) cp); return_error(gs_error_Fatal); } #endif clist_fwrite_chars(cp + 1, cp->size, cfile); } pcl->head = pcl->tail = 0; } clist_fwrite_chars(&end, 1, cfile); process_interrupts(); code_b = clist_ferror_code(bfile); code_c = clist_ferror_code(cfile); if (code_b < 0) return_error(code_b); if (code_c < 0) return_error(code_c); } return code_b | code_c; } /* Write out the buffered commands, and reset the buffer. */ int /* ret 0 all-ok, -ve error code, or +1 ok w/low-mem warning */ cmd_write_buffer(gx_device_clist_writer * cldev, byte cmd_end) { int nbands = cldev->nbands; gx_clist_state *pcls; int band; int code = cmd_write_band(cldev, cldev->band_range_min, cldev->band_range_max, &cldev->band_range_list, cmd_opv_end_run); int warning = code; for (band = 0, pcls = cldev->states; code >= 0 && band < nbands; band++, pcls++ ) { code = cmd_write_band(cldev, band, band, &pcls->list, cmd_end); warning |= code; } /* If an error occurred, finish cleaning up the pointers. */ for (; band < nbands; band++, pcls++) pcls->list.head = pcls->list.tail = 0; cldev->cnext = cldev->cbuf; cldev->ccl = 0; #ifdef DEBUG if (gs_debug_c('l')) cmd_print_stats(); #endif return_check_interrupt(code != 0 ? code : warning); } /* * Add a command to the appropriate band list, and allocate space for its * data. Return the pointer to the data area. If an error or (low-memory * warning) occurs, set cldev->error_code and return 0. */ #define cmd_headroom (sizeof(cmd_prefix) + arch_align_ptr_mod) byte * cmd_put_list_op(gx_device_clist_writer * cldev, cmd_list * pcl, uint size) { byte *dp = cldev->cnext; if (size + cmd_headroom > cldev->cend - dp) { if ((cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) { if (cldev->error_code < 0) cldev->error_is_retryable = 0; /* hard error */ else { /* upgrade lo-mem warning into an error */ if (!cldev->ignore_lo_mem_warnings) cldev->error_code = gs_note_error(gs_error_VMerror); cldev->error_is_retryable = 1; } return 0; } else return cmd_put_list_op(cldev, pcl, size); } if (cldev->ccl == pcl) { /* We're adding another command for the same band. */ /* Tack it onto the end of the previous one. */ cmd_count_add1(stats_cmd.same_band); #ifdef DEBUG if (pcl->tail->size > dp - (byte *) (pcl->tail + 1)) { lprintf1("cmd_put_list_op error at 0x%lx\n", (ulong) pcl->tail); } #endif pcl->tail->size += size; } else { /* Skip to an appropriate alignment boundary. */ /* (We assume the command buffer itself is aligned.) */ cmd_prefix *cp = (cmd_prefix *) (dp + ((cldev->cbuf - dp) & (arch_align_ptr_mod - 1))); cmd_count_add1(stats_cmd.other_band); dp = (byte *) (cp + 1); if (pcl->tail != 0) { #ifdef DEBUG if (pcl->tail < pcl->head || pcl->tail->size > dp - (byte *) (pcl->tail + 1) ) { lprintf1("cmd_put_list_op error at 0x%lx\n", (ulong) pcl->tail); } #endif pcl->tail->next = cp; } else pcl->head = cp; pcl->tail = cp; cldev->ccl = pcl; cp->size = size; } cldev->cnext = dp + size; return dp; } #ifdef DEBUG byte * cmd_put_op(gx_device_clist_writer * cldev, gx_clist_state * pcls, uint size) { if_debug3('L', "[L]band %d: size=%u, left=%u", (int)(pcls - cldev->states), size, (uint) (cldev->cend - cldev->cnext)); return cmd_put_list_op(cldev, &pcls->list, size); } #endif /* Add a command for a range of bands. */ byte * cmd_put_range_op(gx_device_clist_writer * cldev, int band_min, int band_max, uint size) { if_debug4('L', "[L]band range(%d,%d): size=%u, left=%u", band_min, band_max, size, (uint)(cldev->cend - cldev->cnext)); if (cldev->ccl != 0 && (cldev->ccl != &cldev->band_range_list || band_min != cldev->band_range_min || band_max != cldev->band_range_max) ) { if ((cldev->error_code = cmd_write_buffer(cldev, cmd_opv_end_run)) != 0) { if (cldev->error_code < 0) cldev->error_is_retryable = 0; /* hard error */ else { /* upgrade lo-mem warning into an error */ cldev->error_code = gs_error_VMerror; cldev->error_is_retryable = 1; } return 0; } cldev->band_range_min = band_min; cldev->band_range_max = band_max; } return cmd_put_list_op(cldev, &cldev->band_range_list, size); } /* Write a variable-size positive integer. */ int cmd_size_w(register uint w) { register int size = 1; while (w > 0x7f) w >>= 7, size++; return size; } byte * cmd_put_w(register uint w, register byte * dp) { while (w > 0x7f) *dp++ = w | 0x80, w >>= 7; *dp = w; return dp + 1; } /* Define the encodings of the different settable colors. */ const clist_select_color_t clist_select_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 0}, clist_select_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 0}, clist_select_tile_color0 = {cmd_op_set_color0, cmd_opv_delta2_color0, 1}, clist_select_tile_color1 = {cmd_op_set_color1, cmd_opv_delta2_color1, 1}; int cmd_put_color(gx_device_clist_writer * cldev, gx_clist_state * pcls, const clist_select_color_t * select, gx_color_index color, gx_color_index * pcolor) { byte *dp; long diff = (long)color - (long)(*pcolor); byte op, op_delta2; int code; if (diff == 0) return 0; if (select->tile_color) { code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_tile_color, 1); if (code < 0) return code; } op = select->set_op; op_delta2 = select->delta2_op; if (color == gx_no_color_index) { /* * We must handle this specially, because it may take more * bytes than the color depth. */ code = set_cmd_put_op(dp, cldev, pcls, op + 15, 1); if (code < 0) return code; } else { long delta; byte operand; switch ((cldev->color_info.depth + 15) >> 3) { case 5: if (!((delta = diff + cmd_delta1_32_bias) & ~cmd_delta1_32_mask) && (operand = (byte) ((delta >> 23) + ((delta >> 18) & 1))) != 0 && operand != 15 ) { code = set_cmd_put_op(dp, cldev, pcls, (byte) (op + operand), 2); if (code < 0) return code; dp[1] = (byte) (((delta >> 10) & 0300) + (delta >> 5) + delta); break; } if (!((delta = diff + cmd_delta2_32_bias) & ~cmd_delta2_32_mask) ) { code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3); if (code < 0) return code; dp[1] = (byte) ((delta >> 20) + (delta >> 16)); dp[2] = (byte) ((delta >> 4) + delta); break; } code = set_cmd_put_op(dp, cldev, pcls, op, 5); if (code < 0) return code; *++dp = (byte) (color >> 24); goto b3; case 4: if (!((delta = diff + cmd_delta1_24_bias) & ~cmd_delta1_24_mask) && (operand = (byte) (delta >> 16)) != 0 && operand != 15 ) { code = set_cmd_put_op(dp, cldev, pcls, (byte) (op + operand), 2); if (code < 0) return code; dp[1] = (byte) ((delta >> 4) + delta); break; } if (!((delta = diff + cmd_delta2_24_bias) & ~cmd_delta2_24_mask) ) { code = set_cmd_put_op(dp, cldev, pcls, op_delta2, 3); if (code < 0) return code; dp[1] = ((byte) (delta >> 13) & 0xf8) + ((byte) (delta >> 11) & 7); dp[2] = (byte) (((delta >> 3) & 0xe0) + delta); break; } code = set_cmd_put_op(dp, cldev, pcls, op, 4); if (code < 0) return code; b3: *++dp = (byte) (color >> 16); goto b2; case 3: code = set_cmd_put_op(dp, cldev, pcls, op, 3); if (code < 0) return code; b2: *++dp = (byte) (color >> 8); goto b1; case 2: if (diff >= -7 && diff < 7) { code = set_cmd_put_op(dp, cldev, pcls, op + (int)diff + 8, 1); if (code < 0) return code; break; } code = set_cmd_put_op(dp, cldev, pcls, op, 2); if (code < 0) return code; b1: dp[1] = (byte) color; } } *pcolor = color; return 0; } /* Put out a command to set the tile colors. */ int cmd_set_tile_colors(gx_device_clist_writer * cldev, gx_clist_state * pcls, gx_color_index color0, gx_color_index color1) { int code = 0; if (color0 != pcls->tile_colors[0]) { code = cmd_put_color(cldev, pcls, &clist_select_tile_color0, color0, &pcls->tile_colors[0]); if (code != 0) return code; } if (color1 != pcls->tile_colors[1]) code = cmd_put_color(cldev, pcls, &clist_select_tile_color1, color1, &pcls->tile_colors[1]); return code; } /* Put out a command to set the tile phase. */ int cmd_set_tile_phase(gx_device_clist_writer * cldev, gx_clist_state * pcls, int px, int py) { int pcsize; byte *dp; int code; pcsize = 1 + cmd_size2w(px, py); code = set_cmd_put_op(dp, cldev, pcls, (byte)cmd_opv_set_tile_phase, pcsize); if (code < 0) return code; ++dp; pcls->tile_phase.x = px; pcls->tile_phase.y = py; cmd_putxy(pcls->tile_phase, dp); return 0; } /* Write a command to enable or disable the logical operation. */ int cmd_put_enable_lop(gx_device_clist_writer * cldev, gx_clist_state * pcls, int enable) { byte *dp; int code = set_cmd_put_op(dp, cldev, pcls, (byte)(enable ? cmd_opv_enable_lop : cmd_opv_disable_lop), 1); if (code < 0) return code; pcls->lop_enabled = enable; return 0; } /* Write a command to enable or disable clipping. */ /* This routine is only called if the path extensions are included. */ int cmd_put_enable_clip(gx_device_clist_writer * cldev, gx_clist_state * pcls, int enable) { byte *dp; int code = set_cmd_put_op(dp, cldev, pcls, (byte)(enable ? cmd_opv_enable_clip : cmd_opv_disable_clip), 1); if (code < 0) return code; pcls->clip_enabled = enable; return 0; } /* Write a command to set the logical operation. */ int cmd_set_lop(gx_device_clist_writer * cldev, gx_clist_state * pcls, gs_logical_operation_t lop) { byte *dp; uint lop_msb = lop >> 6; int code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc, 2 + cmd_size_w(lop_msb)); if (code < 0) return code; dp[1] = cmd_set_misc_lop + (lop & 0x3f); cmd_put_w(lop_msb, dp + 2); pcls->lop = lop; return 0; } /* Disable (if default) or enable the logical operation, setting it if */ /* needed. */ int cmd_update_lop(gx_device_clist_writer *cldev, gx_clist_state *pcls, gs_logical_operation_t lop) { int code; if (lop == lop_default) return cmd_disable_lop(cldev, pcls); code = cmd_set_lop(cldev, pcls, lop); if (code < 0) return code; return cmd_enable_lop(cldev, pcls); } /* Write a parameter list */ int /* ret 0 all ok, -ve error */ cmd_put_params(gx_device_clist_writer *cldev, gs_param_list *param_list) /* NB open for READ */ { byte *dp; int code; byte local_buf[512]; /* arbitrary */ int param_length; /* Get serialized list's length + try to get it into local var if it fits. */ param_length = code = gs_param_list_serialize(param_list, local_buf, sizeof(local_buf)); if (param_length > 0) { /* Get cmd buffer space for serialized */ code = set_cmd_put_all_op(dp, cldev, cmd_opv_put_params, 1 + sizeof(unsigned) + param_length); if (code < 0) return code; /* write param list to cmd list: needs to all fit in cmd buffer */ if_debug1('l', "[l]put_params, length=%d\n", param_length); ++dp; memcpy(dp, ¶m_length, sizeof(unsigned)); dp += sizeof(unsigned); if (param_length > sizeof(local_buf)) { int old_param_length = param_length; param_length = code = gs_param_list_serialize(param_list, dp, old_param_length); if (param_length >= 0) code = (old_param_length != param_length ? gs_note_error(gs_error_unknownerror) : 0); if (code < 0) { /* error serializing: back out by writing a 0-length parm list */ memset(dp - sizeof(unsigned), 0, sizeof(unsigned)); cmd_shorten_list_op(cldev, &cldev->band_range_list, old_param_length); } } else memcpy(dp, local_buf, param_length); /* did this when computing length */ } return code; } /* Initialize CCITTFax filters. */ private void clist_cf_init(stream_CF_state *ss, int width) { ss->K = -1; ss->Columns = width; ss->EndOfBlock = false; ss->BlackIs1 = true; ss->DecodedByteAlign = align_bitmap_mod; } void clist_cfe_init(stream_CFE_state *ss, int width, gs_memory_t *mem) { s_init_state((stream_state *)ss, &s_CFE_template, mem); s_CFE_set_defaults_inline(ss); clist_cf_init((stream_CF_state *)ss, width); s_CFE_template.init((stream_state *)(ss)); } void clist_cfd_init(stream_CFD_state *ss, int width, int height, gs_memory_t *mem) { s_init_state((stream_state *)ss, &s_CFD_template, mem); s_CFD_template.set_defaults((stream_state *)ss); clist_cf_init((stream_CF_state *)ss, width); ss->Rows = height; s_CFD_template.init((stream_state *)(ss)); } /* Initialize RunLength filters. */ void clist_rle_init(stream_RLE_state *ss) { s_init_state((stream_state *)ss, &s_RLE_template, (gs_memory_t *)0); s_RLE_set_defaults_inline(ss); s_RLE_init_inline(ss); } void clist_rld_init(stream_RLD_state *ss) { s_init_state((stream_state *)ss, &s_RLD_template, (gs_memory_t *)0); s_RLD_set_defaults_inline(ss); s_RLD_init_inline(ss); }