/* Copyright (C) 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: gxshade.c,v 1.6.2.1.2.1 2003/01/17 00:49:04 giles Exp $ */ /* Shading rendering support */ #include "math_.h" #include "gx.h" #include "gserrors.h" #include "gsrect.h" #include "gxcspace.h" #include "gscie.h" /* requires gscspace.h */ #include "gxdevcli.h" #include "gxistate.h" #include "gxdht.h" /* for computing # of different colors */ #include "gxpaint.h" #include "gxshade.h" #include "gsicc.h" /* Define a maximum smoothness value. */ /* smoothness > 0.2 produces severely blocky output. */ #define MAX_SMOOTHNESS 0.2 /* ================ Packed coordinate streams ================ */ /* Forward references */ private int cs_next_packed_value(P3(shade_coord_stream_t *, int, uint *)); private int cs_next_array_value(P3(shade_coord_stream_t *, int, uint *)); private int cs_next_packed_decoded(P4(shade_coord_stream_t *, int, const float[2], float *)); private int cs_next_array_decoded(P4(shade_coord_stream_t *, int, const float[2], float *)); /* Initialize a packed value stream. */ void shade_next_init(shade_coord_stream_t * cs, const gs_shading_mesh_params_t * params, const gs_imager_state * pis) { cs->params = params; cs->pctm = &pis->ctm; if (data_source_is_stream(params->DataSource)) { /* * Reset the data stream iff it is reusable -- either a reusable * file or a reusable string. */ stream *s = cs->s = params->DataSource.data.strm; if ((s->file != 0 && s->file_limit != max_long) || (s->file == 0 && s->strm == 0) ) sreset(s); } else { sread_string(&cs->ds, params->DataSource.data.str.data, params->DataSource.data.str.size); cs->s = &cs->ds; } if (data_source_is_array(params->DataSource)) { cs->get_value = cs_next_array_value; cs->get_decoded = cs_next_array_decoded; } else { cs->get_value = cs_next_packed_value; cs->get_decoded = cs_next_packed_decoded; } cs->left = 0; } /* Get the next (integer) value from a packed value stream. */ /* 1 <= num_bits <= sizeof(uint) * 8. */ private int cs_next_packed_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) { uint bits = cs->bits; int left = cs->left; if (left >= num_bits) { /* We can satisfy this request with the current buffered bits. */ cs->left = left -= num_bits; *pvalue = (bits >> left) & ((1 << num_bits) - 1); } else { /* We need more bits. */ int needed = num_bits - left; uint value = bits & ((1 << left) - 1); /* all the remaining bits */ for (; needed >= 8; needed -= 8) { int b = sgetc(cs->s); if (b < 0) return_error(gs_error_rangecheck); value = (value << 8) + b; } if (needed == 0) { cs->left = 0; *pvalue = value; } else { int b = sgetc(cs->s); if (b < 0) return_error(gs_error_rangecheck); cs->bits = b; cs->left = left = 8 - needed; *pvalue = (value << needed) + (b >> left); } } return 0; } /* Get the next (integer) value from an unpacked array. */ private int cs_next_array_value(shade_coord_stream_t * cs, int num_bits, uint * pvalue) { float value; uint read; if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 || read != sizeof(float) || value < 0 || value >= (1 << num_bits) || value != (int)value ) return_error(gs_error_rangecheck); *pvalue = (uint) value; return 0; } /* Get the next decoded floating point value. */ private int cs_next_packed_decoded(shade_coord_stream_t * cs, int num_bits, const float decode[2], float *pvalue) { uint value; int code = cs->get_value(cs, num_bits, &value); #if ARCH_CAN_SHIFT_FULL_LONG double max_value = (double)(uint) ((1 << num_bits) - 1); #else double max_value = (double)(uint) (num_bits == sizeof(uint) * 8 ? ~0 : ((1 << num_bits) - 1)); #endif if (code < 0) return code; *pvalue = (decode == 0 ? value / max_value : decode[0] + value * (decode[1] - decode[0]) / max_value); return 0; } /* Get the next floating point value from an array, without decoding. */ private int cs_next_array_decoded(shade_coord_stream_t * cs, int num_bits, const float decode[2], float *pvalue) { float value; uint read; if (sgets(cs->s, (byte *)&value, sizeof(float), &read) < 0 || read != sizeof(float) ) return_error(gs_error_rangecheck); *pvalue = value; return 0; } /* Get the next flag value. */ /* Note that this always starts a new data byte. */ int shade_next_flag(shade_coord_stream_t * cs, int BitsPerFlag) { uint flag; int code; cs->left = 0; /* start a new byte if packed */ code = cs->get_value(cs, BitsPerFlag, &flag); return (code < 0 ? code : flag); } /* Get one or more coordinate pairs. */ int shade_next_coords(shade_coord_stream_t * cs, gs_fixed_point * ppt, int num_points) { int num_bits = cs->params->BitsPerCoordinate; const float *decode = cs->params->Decode; int code = 0; int i; for (i = 0; i < num_points; ++i) { float x, y; if ((code = cs->get_decoded(cs, num_bits, decode, &x)) < 0 || (code = cs->get_decoded(cs, num_bits, decode + 2, &y)) < 0 || (code = gs_point_transform2fixed(cs->pctm, x, y, &ppt[i])) < 0 ) break; } return code; } /* Get a color. Currently all this does is look up Indexed colors. */ int shade_next_color(shade_coord_stream_t * cs, float *pc) { const float *decode = cs->params->Decode + 4; /* skip coord decode */ const gs_color_space *pcs = cs->params->ColorSpace; gs_color_space_index index = gs_color_space_get_index(pcs); int num_bits = cs->params->BitsPerComponent; if (index == gs_color_space_index_Indexed) { uint i; int code = cs->get_value(cs, num_bits, &i); if (code < 0) return code; /****** DO INDEXED LOOKUP TO pc[] ******/ } else { int i, code; int ncomp = gs_color_space_num_components(pcs); for (i = 0; i < ncomp; ++i) if ((code = cs->get_decoded(cs, num_bits, decode + i * 2, &pc[i])) < 0) return code; } return 0; } /* Get the next vertex for a mesh element. */ int shade_next_vertex(shade_coord_stream_t * cs, mesh_vertex_t * vertex) { int code = shade_next_coords(cs, &vertex->p, 1); if (code >= 0) code = shade_next_color(cs, vertex->cc); return code; } /* ================ Shading rendering ================ */ /* Initialize the common parts of the recursion state. */ void shade_init_fill_state(shading_fill_state_t * pfs, const gs_shading_t * psh, gx_device * dev, gs_imager_state * pis) { const gs_color_space *pcs = psh->params.ColorSpace; float max_error = min(pis->smoothness, MAX_SMOOTHNESS); /* * There's no point in trying to achieve smoothness beyond what * the device can implement, i.e., the number of representable * colors times the number of halftone levels. */ long num_colors = max(dev->color_info.max_gray, dev->color_info.max_color) + 1; const gs_range *ranges = 0; int ci; pfs->dev = dev; pfs->pis = pis; pfs->num_components = gs_color_space_num_components(pcs); top: switch ( gs_color_space_get_index(pcs) ) { case gs_color_space_index_Indexed: pcs = gs_cspace_base_space(pcs); goto top; case gs_color_space_index_CIEDEFG: ranges = pcs->params.defg->RangeDEFG.ranges; break; case gs_color_space_index_CIEDEF: ranges = pcs->params.def->RangeDEF.ranges; break; case gs_color_space_index_CIEABC: ranges = pcs->params.abc->RangeABC.ranges; break; case gs_color_space_index_CIEA: ranges = &pcs->params.a->RangeA; break; case gs_color_space_index_CIEICC: ranges = pcs->params.icc.picc_info->Range.ranges; default: break; } if (num_colors <= 32) { /****** WRONG FOR MULTI-PLANE HALFTONES ******/ num_colors *= pis->dev_ht->order.num_levels; } if (max_error < 1.0 / num_colors) max_error = 1.0 / num_colors; for (ci = 0; ci < pfs->num_components; ++ci) pfs->cc_max_error[ci] = (ranges == 0 ? max_error : max_error * (ranges[ci].rmax - ranges[ci].rmin)); } /* Transform a bounding box into device space. */ int shade_bbox_transform2fixed(const gs_rect * rect, const gs_imager_state * pis, gs_fixed_rect * rfixed) { gs_rect dev_rect; int code = gs_bbox_transform(rect, &ctm_only(pis), &dev_rect); if (code >= 0) { rfixed->p.x = float2fixed(dev_rect.p.x); rfixed->p.y = float2fixed(dev_rect.p.y); rfixed->q.x = float2fixed(dev_rect.q.x); rfixed->q.y = float2fixed(dev_rect.q.y); } return code; } /* Fill one piece of a shading. */ int shade_fill_path(const shading_fill_state_t * pfs, gx_path * ppath, gx_device_color * pdevc) { gx_fill_params params; params.rule = -1; /* irrelevant */ params.adjust = pfs->pis->fill_adjust; params.flatness = 0; /* irrelevant */ params.fill_zero_width = false; return (*dev_proc(pfs->dev, fill_path)) (pfs->dev, pfs->pis, ppath, ¶ms, pdevc, NULL); }