/* 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: gscrdp.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* CIE color rendering dictionary creation */ #include "math_.h" #include "memory_.h" #include "string_.h" #include "gx.h" #include "gsdevice.h" #include "gserrors.h" #include "gsmatrix.h" /* for gscolor2.h */ #include "gsstruct.h" #include "gxcspace.h" #include "gscolor2.h" /* for gs_set/currentcolorrendering */ #include "gscrdp.h" #include "gxarith.h" /* ---------------- Writing ---------------- */ /* Internal procedures for writing parameter values. */ private void store_vector3(float *p, const gs_vector3 * pvec) { p[0] = pvec->u, p[1] = pvec->v, p[2] = pvec->w; } private int write_floats(gs_param_list * plist, gs_param_name key, const float *values, int size, gs_memory_t * mem) { float *p = (float *) gs_alloc_byte_array(mem, size, sizeof(float), "write_floats"); gs_param_float_array fa; if (p == 0) return_error(gs_error_VMerror); memcpy(p, values, size * sizeof(float)); fa.data = p; fa.size = size; fa.persistent = true; return param_write_float_array(plist, key, &fa); } private int write_vector3(gs_param_list * plist, gs_param_name key, const gs_vector3 * pvec, gs_memory_t * mem) { float values[3]; store_vector3(values, pvec); return write_floats(plist, key, values, 3, mem); } private int write_matrix3(gs_param_list * plist, gs_param_name key, const gs_matrix3 * pmat, gs_memory_t * mem) { float values[9]; if (!memcmp(pmat, &Matrix3_default, sizeof(*pmat))) return 0; store_vector3(values, &pmat->cu); store_vector3(values + 3, &pmat->cv); store_vector3(values + 6, &pmat->cw); return write_floats(plist, key, values, 9, mem); } private int write_range3(gs_param_list * plist, gs_param_name key, const gs_range3 * prange, gs_memory_t * mem) { float values[6]; if (!memcmp(prange, &Range3_default, sizeof(*prange))) return 0; values[0] = prange->ranges[0].rmin, values[1] = prange->ranges[0].rmax; values[2] = prange->ranges[1].rmin, values[3] = prange->ranges[1].rmax; values[4] = prange->ranges[2].rmin, values[5] = prange->ranges[2].rmax; return write_floats(plist, key, values, 6, mem); } private int write_proc3(gs_param_list * plist, gs_param_name key, const gs_cie_render * pcrd, const gs_cie_render_proc3 * procs, const gs_range3 * domain, gs_memory_t * mem) { float *values; uint size = gx_cie_cache_size; gs_param_float_array fa; int i; if (!memcmp(procs, &Encode_default, sizeof(*procs))) return 0; values = (float *)gs_alloc_byte_array(mem, size * 3, sizeof(float), "write_proc3"); if (values == 0) return_error(gs_error_VMerror); for (i = 0; i < 3; ++i) { double base = domain->ranges[i].rmin; double scale = (domain->ranges[i].rmax - base) / (size - 1); int j; for (j = 0; j < size; ++j) values[i * size + j] = (*procs->procs[i]) (j * scale + base, pcrd); } fa.data = values; fa.size = size * 3; fa.persistent = true; return param_write_float_array(plist, key, &fa); } /* Write a CRD as a device parameter. */ int param_write_cie_render1(gs_param_list * plist, gs_param_name key, gs_cie_render * pcrd, gs_memory_t * mem) { gs_param_dict dict; int code, dcode; dict.size = 20; if ((code = param_begin_write_dict(plist, key, &dict, false)) < 0) return code; code = param_put_cie_render1(dict.list, pcrd, mem); dcode = param_end_write_dict(plist, key, &dict); return (code < 0 ? code : dcode); } /* Write a CRD directly to a parameter list. */ int param_put_cie_render1(gs_param_list * plist, gs_cie_render * pcrd, gs_memory_t * mem) { int crd_type = GX_DEVICE_CRD1_TYPE; int code = gs_cie_render_sample(pcrd); /* we need RenderTableT_is_id' */ if (code < 0) return code; if (pcrd->TransformPQR.proc_name) { gs_param_string pn, pd; param_string_from_string(pn, pcrd->TransformPQR.proc_name); pn.size++; /* include terminating null */ pd.data = pcrd->TransformPQR.proc_data.data; pd.size = pcrd->TransformPQR.proc_data.size; pd.persistent = true; /****** WRONG ******/ if ((code = param_write_name(plist, "TransformPQRName", &pn)) < 0 || (code = param_write_string(plist, "TransformPQRData", &pd)) < 0 ) return code; } else if (pcrd->TransformPQR.proc != TransformPQR_default.proc) { /* We have no way to represent the procedure, so return an error. */ return_error(gs_error_rangecheck); } if ((code = param_write_int(plist, "ColorRenderingType", &crd_type)) < 0 || (code = write_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, mem)) < 0 ) return code; if (memcmp(&pcrd->points.BlackPoint, &BlackPoint_default, sizeof(pcrd->points.BlackPoint))) { if ((code = write_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, mem)) < 0) return code; } if ((code = write_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR, mem)) < 0 || (code = write_range3(plist, "RangePQR", &pcrd->RangePQR, mem)) < 0 || /* TransformPQR is handled separately */ (code = write_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN, mem)) < 0 || (code = write_proc3(plist, "EncodeLMNValues", pcrd, &pcrd->EncodeLMN, &pcrd->DomainLMN, mem)) < 0 || (code = write_range3(plist, "RangeLMN", &pcrd->RangeLMN, mem)) < 0 || (code = write_matrix3(plist, "MatrixABC", &pcrd->MatrixABC, mem)) < 0 || (code = write_proc3(plist, "EncodeABCValues", pcrd, &pcrd->EncodeABC, &pcrd->DomainABC, mem)) < 0 || (code = write_range3(plist, "RangeABC", &pcrd->RangeABC, mem)) < 0 ) return code; if (pcrd->RenderTable.lookup.table) { int n = pcrd->RenderTable.lookup.n; int m = pcrd->RenderTable.lookup.m; int na = pcrd->RenderTable.lookup.dims[0]; int *size = (int *) gs_alloc_byte_array(mem, n + 1, sizeof(int), "RenderTableSize"); /* * In principle, we should use gs_alloc_struct_array with a * type descriptor for gs_param_string. However, it is widely * assumed that parameter lists are transient, and don't require * accurate GC information; so we can get away with allocating * the string table as bytes. */ gs_param_string *table = (gs_param_string *) gs_alloc_byte_array(mem, na, sizeof(gs_param_string), "RenderTableTable"); gs_param_int_array ia; if (size == 0 || table == 0) code = gs_note_error(gs_error_VMerror); else { memcpy(size, pcrd->RenderTable.lookup.dims, sizeof(int) * n); size[n] = m; ia.data = size; ia.size = n + 1; ia.persistent = true; code = param_write_int_array(plist, "RenderTableSize", &ia); } if (code >= 0) { gs_param_string_array sa; int a; for (a = 0; a < na; ++a) table[a].data = pcrd->RenderTable.lookup.table[a].data, table[a].size = pcrd->RenderTable.lookup.table[a].size, table[a].persistent = true; sa.data = table; sa.size = na; sa.persistent = true; code = param_write_string_array(plist, "RenderTableTable", &sa); if (code >= 0 && !pcrd->caches.RenderTableT_is_identity) { /****** WRITE RenderTableTValues LIKE write_proc3 ******/ uint size = gx_cie_cache_size; float *values = (float *)gs_alloc_byte_array(mem, size * m, sizeof(float), "write_proc3"); gs_param_float_array fa; int i; if (values == 0) return_error(gs_error_VMerror); for (i = 0; i < m; ++i) { double scale = 255.0 / (size - 1); int j; for (j = 0; j < size; ++j) values[i * size + j] = frac2float((*pcrd->RenderTable.T.procs[i]) (j * scale, pcrd)); } fa.data = values; fa.size = size * m; fa.persistent = true; code = param_write_float_array(plist, "RenderTableTValues", &fa); } } if (code < 0) { gs_free_object(mem, table, "RenderTableTable"); gs_free_object(mem, size, "RenderTableSize"); return code; } } return code; } /* ---------------- Reading ---------------- */ /* Internal procedures for reading parameter values. */ private void load_vector3(gs_vector3 * pvec, const float *p) { pvec->u = p[0], pvec->v = p[1], pvec->w = p[2]; } private int read_floats(gs_param_list * plist, gs_param_name key, float *values, int count) { gs_param_float_array fa; int code = param_read_float_array(plist, key, &fa); if (code) return code; if (fa.size != count) return_error(gs_error_rangecheck); memcpy(values, fa.data, sizeof(float) * count); return 0; } private int read_vector3(gs_param_list * plist, gs_param_name key, gs_vector3 * pvec, const gs_vector3 * dflt) { float values[3]; int code = read_floats(plist, key, values, 3); switch (code) { case 1: /* not defined */ if (dflt) *pvec = *dflt; break; case 0: load_vector3(pvec, values); default: /* error */ break; } return code; } private int read_matrix3(gs_param_list * plist, gs_param_name key, gs_matrix3 * pmat) { float values[9]; int code = read_floats(plist, key, values, 9); switch (code) { case 1: /* not defined */ *pmat = Matrix3_default; break; case 0: load_vector3(&pmat->cu, values); load_vector3(&pmat->cv, values + 3); load_vector3(&pmat->cw, values + 6); default: /* error */ break; } return code; } private int read_range3(gs_param_list * plist, gs_param_name key, gs_range3 * prange) { float values[6]; int code = read_floats(plist, key, values, 6); switch (code) { case 1: /* not defined */ *prange = Range3_default; break; case 0: prange->ranges[0].rmin = values[0]; prange->ranges[0].rmax = values[1]; prange->ranges[1].rmin = values[2]; prange->ranges[1].rmax = values[3]; prange->ranges[2].rmin = values[4]; prange->ranges[2].rmax = values[5]; default: /* error */ break; } return code; } private int read_proc3(gs_param_list * plist, gs_param_name key, float values[gx_cie_cache_size * 3]) { return read_floats(plist, key, values, gx_cie_cache_size * 3); } /* Read a CRD from a device parameter. */ int gs_cie_render1_param_initialize(gs_cie_render * pcrd, gs_param_list * plist, gs_param_name key, gx_device * dev) { gs_param_dict dict; int code = param_begin_read_dict(plist, key, &dict, false); int dcode; if (code < 0) return code; code = param_get_cie_render1(pcrd, dict.list, dev); dcode = param_end_read_dict(plist, key, &dict); if (code < 0) return code; if (dcode < 0) return dcode; gs_cie_render_init(pcrd); gs_cie_render_sample(pcrd); return gs_cie_render_complete(pcrd); } /* Define the structure for passing Encode values as "client data". */ typedef struct encode_data_s { float lmn[gx_cie_cache_size * 3]; /* EncodeLMN */ float abc[gx_cie_cache_size * 3]; /* EncodeABC */ float t[gx_cie_cache_size * 4]; /* RenderTable.T */ } encode_data_t; /* Define procedures that retrieve the Encode values read from the list. */ private float encode_from_data(floatp v, const float values[gx_cie_cache_size], const gs_range * range) { return (v <= range->rmin ? values[0] : v >= range->rmax ? values[gx_cie_cache_size - 1] : values[(int)((v - range->rmin) / (range->rmax - range->rmin) * (gx_cie_cache_size - 1) + 0.5)]); } /* * The repetitive boilerplate in the next 10 procedures really sticks in * my craw, but I've got a mandate not to use macros.... */ private float encode_lmn_0_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->lmn[0], &pcrd->DomainLMN.ranges[0]); } private float encode_lmn_1_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->lmn[gx_cie_cache_size], &pcrd->DomainLMN.ranges[1]); } private float encode_lmn_2_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->lmn[gx_cie_cache_size * 2], &pcrd->DomainLMN.ranges[2]); } private float encode_abc_0_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->abc[0], &pcrd->DomainABC.ranges[0]); } private float encode_abc_1_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->abc[gx_cie_cache_size], &pcrd->DomainABC.ranges[1]); } private float encode_abc_2_from_data(floatp v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return encode_from_data(v, &data->abc[gx_cie_cache_size * 2], &pcrd->DomainABC.ranges[2]); } private frac render_table_t_0_from_data(byte v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return float2frac(encode_from_data(v / 255.0, &data->t[0], &Range3_default.ranges[0])); } private frac render_table_t_1_from_data(byte v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return float2frac(encode_from_data(v / 255.0, &data->t[gx_cie_cache_size], &Range3_default.ranges[0])); } private frac render_table_t_2_from_data(byte v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return float2frac(encode_from_data(v / 255.0, &data->t[gx_cie_cache_size * 2], &Range3_default.ranges[0])); } private frac render_table_t_3_from_data(byte v, const gs_cie_render * pcrd) { const encode_data_t *data = pcrd->client_data; return float2frac(encode_from_data(v / 255.0, &data->t[gx_cie_cache_size * 3], &Range3_default.ranges[0])); } private const gs_cie_render_proc3 EncodeLMN_from_data = { {encode_lmn_0_from_data, encode_lmn_1_from_data, encode_lmn_2_from_data} }; private const gs_cie_render_proc3 EncodeABC_from_data = { {encode_abc_0_from_data, encode_abc_1_from_data, encode_abc_2_from_data} }; private const gs_cie_render_table_procs RenderTableT_from_data = { {render_table_t_0_from_data, render_table_t_1_from_data, render_table_t_2_from_data, render_table_t_3_from_data } }; /* Read a CRD directly from a parameter list. */ int param_get_cie_render1(gs_cie_render * pcrd, gs_param_list * plist, gx_device * dev) { encode_data_t data; gs_param_int_array rt_size; int crd_type; int code, code_lmn, code_abc, code_rt, code_t; gs_param_string pname, pdata; /* Reset the status to invalidate cached information. */ pcrd->status = CIE_RENDER_STATUS_BUILT; if ((code = param_read_int(plist, "ColorRenderingType", &crd_type)) < 0 || crd_type != GX_DEVICE_CRD1_TYPE || (code = read_vector3(plist, "WhitePoint", &pcrd->points.WhitePoint, NULL)) < 0 || (code = read_vector3(plist, "BlackPoint", &pcrd->points.BlackPoint, &BlackPoint_default)) < 0 || (code = read_matrix3(plist, "MatrixPQR", &pcrd->MatrixPQR)) < 0 || (code = read_range3(plist, "RangePQR", &pcrd->RangePQR)) < 0 || /* TransformPQR is handled specially below. */ (code = read_matrix3(plist, "MatrixLMN", &pcrd->MatrixLMN)) < 0 || (code_lmn = code = read_proc3(plist, "EncodeLMNValues", data.lmn)) < 0 || (code = read_range3(plist, "RangeLMN", &pcrd->RangeLMN)) < 0 || (code = read_matrix3(plist, "MatrixABC", &pcrd->MatrixABC)) < 0 || (code_abc = code = read_proc3(plist, "EncodeABCValues", data.abc)) < 0 || (code = read_range3(plist, "RangeABC", &pcrd->RangeABC)) < 0 ) return code; /* Handle the sampled functions. */ switch (code = param_read_string(plist, "TransformPQRName", &pname)) { default: /* error */ return code; case 1: /* missing */ pcrd->TransformPQR = TransformPQR_default; break; case 0: /* specified */ /* The procedure name must be null-terminated: */ /* see param_put_cie_render1 above. */ if (pname.size < 1 || pname.data[pname.size - 1] != 0) return_error(gs_error_rangecheck); pcrd->TransformPQR.proc = TransformPQR_lookup_proc_name; pcrd->TransformPQR.proc_name = (char *)pname.data; switch (code = param_read_string(plist, "TransformPQRData", &pdata)) { default: /* error */ return code; case 1: /* missing */ pcrd->TransformPQR.proc_data.data = 0; pcrd->TransformPQR.proc_data.size = 0; break; case 0: pcrd->TransformPQR.proc_data.data = pdata.data; pcrd->TransformPQR.proc_data.size = pdata.size; } pcrd->TransformPQR.driver_name = gs_devicename(dev); break; } pcrd->client_data = &data; if (code_lmn > 0) pcrd->EncodeLMN = Encode_default; else pcrd->EncodeLMN = EncodeLMN_from_data; if (code_abc > 0) pcrd->EncodeABC = Encode_default; else pcrd->EncodeABC = EncodeABC_from_data; code_rt = code = param_read_int_array(plist, "RenderTableSize", &rt_size); if (code == 1) { if (pcrd->RenderTable.lookup.table) { gs_free_object(pcrd->rc.memory, (void *)pcrd->RenderTable.lookup.table, /* break const */ "param_get_cie_render1(RenderTable)"); pcrd->RenderTable.lookup.table = 0; } pcrd->RenderTable.T = RenderTableT_default; code_t = 1; } else if (code < 0) return code; else if (rt_size.size != 4) return_error(gs_error_rangecheck); else { gs_param_string_array rt_values; gs_const_string *table; int n, m, j; for (j = 0; j < rt_size.size; ++j) if (rt_size.data[j] < 1) return_error(gs_error_rangecheck); code = param_read_string_array(plist, "RenderTableTable", &rt_values); if (code < 0) return code; if (code > 0 || rt_values.size != rt_size.data[0]) return_error(gs_error_rangecheck); /* Note: currently n = 3 (rt_size.size = 4) always. */ for (j = 0; j < rt_values.size; ++j) if (rt_values.data[j].size != rt_size.data[1] * rt_size.data[2] * rt_size.data[3]) return_error(gs_error_rangecheck); pcrd->RenderTable.lookup.n = n = rt_size.size - 1; pcrd->RenderTable.lookup.m = m = rt_size.data[n]; if (n > 4 || m > 4) return_error(gs_error_rangecheck); memcpy(pcrd->RenderTable.lookup.dims, rt_size.data, n * sizeof(int)); table = gs_alloc_struct_array(pcrd->rc.memory, pcrd->RenderTable.lookup.dims[0], gs_const_string, &st_const_string_element, "RenderTable table"); if (table == 0) return_error(gs_error_VMerror); for (j = 0; j < pcrd->RenderTable.lookup.dims[0]; ++j) { table[j].data = rt_values.data[j].data; table[j].size = rt_values.data[j].size; } pcrd->RenderTable.lookup.table = table; pcrd->RenderTable.T = RenderTableT_from_data; code_t = code = read_floats(plist, "RenderTableTValues", data.t, gx_cie_cache_size * m); if (code > 0) pcrd->RenderTable.T = RenderTableT_default; else if (code == 0) pcrd->RenderTable.T = RenderTableT_from_data; } if ((code = gs_cie_render_init(pcrd)) >= 0 && (code = gs_cie_render_sample(pcrd)) >= 0 ) code = gs_cie_render_complete(pcrd); /* Clean up before exiting. */ pcrd->client_data = 0; if (code_lmn == 0) pcrd->EncodeLMN = EncodeLMN_from_cache; if (code_abc == 0) pcrd->EncodeABC = EncodeABC_from_cache; if (code_t == 0) pcrd->RenderTable.T = RenderTableT_from_cache; return code; }