/src/ghostpdl/pcl/pcl/pccsbase.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* pccsbase.h - base color space code for PCL 5c */ |
18 | | |
19 | | #include "gx.h" |
20 | | #include "math_.h" |
21 | | #include "gstypes.h" |
22 | | #include "gsmatrix.h" |
23 | | #include "gsstruct.h" |
24 | | #include "gsrefct.h" |
25 | | #include "gscspace.h" |
26 | | #include "gscolor2.h" |
27 | | #include "gscie.h" |
28 | | #include "pcmtx3.h" |
29 | | #include "pccsbase.h" |
30 | | #include "pcstate.h" |
31 | | |
32 | | /* for RC structures */ |
33 | | gs_private_st_simple(st_cs_base_t, pcl_cs_base_t, "pcl base color space"); |
34 | | |
35 | | |
36 | | /* |
37 | | * Handle min/max values for device-independent color spaces. |
38 | | * |
39 | | * Examples in HP's "PCL 5 Color Technical Reference Manual" are unclear |
40 | | * about the interpretation of minimum/maximum value for components for the |
41 | | * device independent color spaces. It is clear that the "raw" input range |
42 | | * for these parameters is always [ 0, 255 ], but how this range is mapped |
43 | | * is not fully obvious. |
44 | | * |
45 | | * Empirical observations with both the CIE L*a*b* and the luminance- |
46 | | * chrominance color space do little to clear up this confusion. In the range |
47 | | * [ 0, 255 ] (as suggested in the "PCL 5 Color Technical Reference Manual"), |
48 | | * integer arithmetic overflow seems to occur at some points, leading to |
49 | | * rather curious color progressions (a moderate brown changes abruptly to |
50 | | * a dark green at the half-intensity point of a "gray" scale). |
51 | | * |
52 | | * Side Note: |
53 | | * Device dependent color spaces in PCL 5c are not provided with |
54 | | * ranges, but are assigned white and black points. The interpretation of |
55 | | * these points is clear: specify the white point to get the maximum |
56 | | * intensity value for all components on the device, and the black point |
57 | | * to achieve the minimum value (for printers these are reasonably white |
58 | | * and black; most screens are adjusted to achieve the same result, |
59 | | * though there is no strict requirement for this to be the case). |
60 | | * Values within this range [black_point, white_point] are mapped |
61 | | * by the obvious linear transformation; values outside of the range |
62 | | * are clamped to the nearest boundary point. |
63 | | * |
64 | | * Two items of note for device dependent color spaces: |
65 | | * |
66 | | * a CMY color space is just an RGB color space with the white and |
67 | | * black points inverted. |
68 | | * |
69 | | * For a given value of bits-per-primary, it is quite possible to |
70 | | * set the white and black points so that one or both may not be |
71 | | * achievable |
72 | | * |
73 | | * In this implementation, the white and black points of the device- |
74 | | * specific color spaces are handled in the initial "normalization" |
75 | | * step, before colors are entered into the palette. |
76 | | * |
77 | | * To do something sensible for device independent color space ranges, it is |
78 | | * ncessary to ignore HP's implementation and ask what applications might |
79 | | * reasonably want to do with the range parameters. The answer depends on the |
80 | | * particular color space: |
81 | | * |
82 | | * a. For colorimetric RGB spaces, the reasonable assumption is that the |
83 | | * parameters correspond to the range of the input primaries, from |
84 | | * minimal 0 to full intensity. Furthermore, the white point corresponds |
85 | | * to the full intensity for each primary, the black point to minimum |
86 | | * intensity. |
87 | | * |
88 | | * The difficulty with this interpretation is that it renders the range |
89 | | * parameters meaningless. PCL requires input data for device-independent |
90 | | * color spaces to be in the range [0, 255], with 0 ==> minimum value and |
91 | | * 255 ==> maximum value. Combined with the assumption above, this will |
92 | | * always map the "raw" input values {0, 0, 0} and {255, 255, 255} to |
93 | | * the black and white points, respectively. |
94 | | * |
95 | | * To avoid making the range parameters completely meaningless in this |
96 | | * case, we will actually use a different interpretation. The modification |
97 | | * continues to map the raw input values such that 0 ==> minimum value |
98 | | * and 255 ==> maximum value, but the black and white points continue |
99 | | * to be {0, 0, 0} and {1, 1, 1}. Values outside of this range are |
100 | | * clipped. |
101 | | * |
102 | | * To the extent that we can determine, this interpretation bears some |
103 | | * relationship to that used by HP. |
104 | | * |
105 | | * b. For the CIE L*a*b* space, the interpretation of the value of each |
106 | | * component is fixed by standards, so the ranges specified in the |
107 | | * Configure Image Data command can only be interpreted as indicating |
108 | | * which set of values the "raw" input range [0, 255] should be mapped |
109 | | * to (0 ==> min_value, 255 ==> max value) |
110 | | * |
111 | | * c. For consistency it is necessary to handle the range parameters for |
112 | | * luminance-chrominance in the same manner as they are handled for the |
113 | | * colorimetric RGB case. This approach makes even less sense in this |
114 | | * case than for the colorimetric RGB case, as the region of input space |
115 | | * that corresponds to real colors for luminance-chrominance spaces is |
116 | | * not a cube (i.e.: it is possible for each of the components to be |
117 | | * in a reasonable range, but for the combination to yield primary |
118 | | * component values < 0 or > 1). There is not much choice about this |
119 | | * arrangement, however, because a luminance-chrominance space can be |
120 | | * set up to directly mimic a colorimetric RGB space by setting the |
121 | | * transformation between the two to the identity transformation. |
122 | | * |
123 | | * For all of these range mappings, the initial step is to map the "raw" input |
124 | | * range to [min_val, max_val], and incorporate the effect of the lookup |
125 | | * table for the particular color space (if any). This is accomplished by the |
126 | | * following macro. Note that the initial normalization step has already |
127 | | * converted the "raw" input range to [0, 1] (the same range as is used by |
128 | | * device-dependent color spaces). |
129 | | */ |
130 | | |
131 | | #define convert_val(val, min_val, range, plktbl) \ |
132 | 0 | BEGIN \ |
133 | 0 | if ((plktbl) != 0) \ |
134 | 0 | val = (double)(plktbl)[(int)(255 * val)] / 255.0; \ |
135 | 0 | val = min_val + val * range; \ |
136 | 0 | END |
137 | | |
138 | | /* |
139 | | * Default Configure Image Data information for the various color spaces. |
140 | | * |
141 | | * The black and white points for device dependent color spaces are not included |
142 | | * here as those are handled via palette value normalization, not via the color |
143 | | * space. Since the black and white points are the only parameters for the |
144 | | * device-specific color spaces, there is no default information here for them |
145 | | * at all. |
146 | | * |
147 | | * Other color spaces have up to three sets of default information: |
148 | | * |
149 | | * default parameter ranges (all device-independent color spaces) |
150 | | * |
151 | | * the default chromaticity of the primaries (colorimetric RGB and |
152 | | * luminance-chrominance spaces) |
153 | | * |
154 | | * the default conversion between the color components and the primaries |
155 | | * for which chromaticities have been provided (only luminance- |
156 | | * chrominance space) |
157 | | */ |
158 | | static const pcl_cid_minmax_t cielab_range_default = { |
159 | | {{0.0f, 100.0f}, {-100.0f, 100.0f}, {-100.0f, 100.0f}} |
160 | | }; |
161 | | |
162 | | static const pcl_cid_minmax_t colmet_range_default = { |
163 | | {{0.0f, 1.0f}, {0.0f, 1.0f}, {0.0f, 1.0f}} |
164 | | }; |
165 | | |
166 | | static const pcl_cid_minmax_t lumchrom_range_default = { |
167 | | {{0.0f, 1.0f}, {-0.89f, 0.89f}, {-0.70f, 0.70f}} |
168 | | }; |
169 | | |
170 | | static const pcl_cid_col_common_t chroma_default = { |
171 | | { |
172 | | {0.640f, 0.340f}, /* "red" primary chromaticity */ |
173 | | {0.310f, 0.595f}, /* "green" primary chromaticity */ |
174 | | {0.155f, 0.070f}, /* "blue" chromaticity */ |
175 | | {0.313f, 0.329f} /* white chromaticity */ |
176 | | }, |
177 | | {{1, 1.0f}, {1, 1.0f}, {1, 1.0f}} |
178 | | }; |
179 | | |
180 | | static const float lumchrom_xform_default[9] = { |
181 | | 0.30f, 0.59f, 0.11f, -0.30f, -0.59f, 0.89f, 0.70f, -0.59f, -0.11f |
182 | | }; |
183 | | |
184 | | /* structure of default values for all color spaces */ |
185 | | static const struct |
186 | | { |
187 | | const pcl_cid_minmax_t *pminmax; |
188 | | const pcl_cid_col_common_t *pchroma; |
189 | | const float *pxform; |
190 | | } cid_data_default[(int)pcl_cspace_num] = { |
191 | | {0, 0, 0}, /* pcl_cspace_RGB */ |
192 | | {0, 0, 0}, /* pcl_cspace_CMY */ |
193 | | {&colmet_range_default, &chroma_default, 0}, /* pcl_cspace_Colorimetric */ |
194 | | {&cielab_range_default, 0, 0}, /* pcl_cspace_CIELab */ |
195 | | {&lumchrom_range_default, &chroma_default, lumchrom_xform_default} /* pcl_cspace_LumChrom */ |
196 | | }; |
197 | | |
198 | | /* |
199 | | * Code for constructing/modifying the client data structure of PCL base |
200 | | * color spaces. |
201 | | */ |
202 | | |
203 | | /* |
204 | | * Set the range parameters for a color space. |
205 | | */ |
206 | | static void |
207 | | set_client_info_range(pcl_cs_client_data_t * pdata, |
208 | | const pcl_cid_minmax_t * pminmax) |
209 | 0 | { |
210 | 0 | int i; |
211 | |
|
212 | 0 | for (i = 0; i < 3; i++) { |
213 | 0 | pdata->min_val[i] = pminmax->val_range[i].min_val; |
214 | 0 | pdata->range[i] = pminmax->val_range[i].max_val |
215 | 0 | - pminmax->val_range[i].min_val; |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | /* |
220 | | * Set the gamma/gain information for a color space. |
221 | | */ |
222 | | static void |
223 | | set_client_info_chroma(pcl_cs_client_data_t * pdata, |
224 | | const pcl_cid_col_common_t * pchroma) |
225 | 0 | { |
226 | 0 | int i; |
227 | |
|
228 | 0 | for (i = 0; i < 3; i++) { |
229 | 0 | double gamma = pchroma->nonlin[i].gamma; |
230 | |
|
231 | 0 | double gain = pchroma->nonlin[i].gain; |
232 | |
|
233 | 0 | pdata->inv_gamma[i] = (gamma == 0.0 ? 1.0 : 1.0 / gamma); |
234 | 0 | pdata->inv_gain[i] = (gain == 0.0 ? 1.0 : 1.0 / gain); |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | /* |
239 | | * Build the client information structure for a color space. |
240 | | */ |
241 | | static void |
242 | | build_client_data(pcl_cs_client_data_t * pdata, |
243 | | const pcl_cid_data_t * pcid, gs_memory_t * pmem) |
244 | 12.5k | { |
245 | 12.5k | pcl_cspace_type_t type = pcl_cid_get_cspace(pcid); |
246 | 12.5k | const pcl_cid_minmax_t *pminmax = cid_data_default[type].pminmax; |
247 | 12.5k | const pcl_cid_col_common_t *pchroma = cid_data_default[type].pchroma; |
248 | | |
249 | | /* see if we have long-form information for device-independent spaces */ |
250 | 12.5k | if (pcid->len > 6) { |
251 | 0 | if (type == pcl_cspace_Colorimetric) { |
252 | 0 | pminmax = &(pcid->u.col.minmax); |
253 | 0 | pchroma = &(pcid->u.col.colmet); |
254 | 0 | } else if (type == pcl_cspace_CIELab) |
255 | 0 | pminmax = &(pcid->u.lab.minmax); |
256 | 0 | else if (type == pcl_cspace_LumChrom) { |
257 | 0 | pminmax = &(pcid->u.lum.minmax); |
258 | 0 | pchroma = &(pcid->u.col.colmet); |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | /* set the range and gamma/gain parameters, as required */ |
263 | 12.5k | if (pminmax != 0) |
264 | 0 | set_client_info_range(pdata, pminmax); |
265 | 12.5k | if (pchroma != 0) |
266 | 0 | set_client_info_chroma(pdata, pchroma); |
267 | 12.5k | } |
268 | | |
269 | | /* |
270 | | * Init a client data structure from an existing client data structure. |
271 | | */ |
272 | | static void |
273 | | init_client_data_from(pcl_cs_client_data_t * pnew, |
274 | | const pcl_cs_client_data_t * pfrom) |
275 | 0 | { |
276 | 0 | *pnew = *pfrom; |
277 | 0 | pcl_lookup_tbl_init_from(pnew->plktbl1, pfrom->plktbl1); |
278 | 0 | pcl_lookup_tbl_init_from(pnew->plktbl2, pfrom->plktbl2); |
279 | 0 | } |
280 | | |
281 | | /* |
282 | | * Update the lookup table information in a PCL base color space. |
283 | | */ |
284 | | static void |
285 | | update_lookup_tbls(pcl_cs_client_data_t * pdata, |
286 | | pcl_lookup_tbl_t * plktbl1, pcl_lookup_tbl_t * plktbl2) |
287 | 0 | { |
288 | 0 | pcl_lookup_tbl_copy_from(pdata->plktbl1, plktbl1); |
289 | 0 | pcl_lookup_tbl_copy_from(pdata->plktbl2, plktbl2); |
290 | 0 | } |
291 | | |
292 | | /* |
293 | | * Free a client data structure. This releases the lookup tables, if they |
294 | | * are present. |
295 | | */ |
296 | | #define free_lookup_tbls(pdata) \ |
297 | | update_lookup_tbls((pdata), NULL, NULL) |
298 | | |
299 | | /* |
300 | | * The colorimetric case. |
301 | | * |
302 | | * The information provided with this color space consists of the CIE (x, y) |
303 | | * chromaticities of the three primaries, and the white point. In order to |
304 | | * derive a color space from this information, three additional assumptions |
305 | | * are required: |
306 | | * |
307 | | * the intensity (Y value) or the white point is 1.0 (identical to the |
308 | | * convention used by PostScript) |
309 | | * |
310 | | * the white point is achieved by setting each of the primaries to its |
311 | | * maximum value (1.0) |
312 | | * |
313 | | * the black point (in this case, { 0, 0, 0 }, since it is not specified) |
314 | | * is achieved by setting each of the primaries to its minimum value |
315 | | * (0.0) |
316 | | * |
317 | | * Relaxing the former assumption would only modify the mapping provided by the |
318 | | * color space by a multiplicative constant. The assumption is also reasonable |
319 | | * for a printing device: even under the strongest reasonable assumptions, the |
320 | | * actual intensity achieved by printed output is determined by the intensity |
321 | | * of the illuminant and the reflectivity of the paper, neither one of which is |
322 | | * known to the color space. Hence, the value of Y selected is arbitrary (so |
323 | | * long as it is > 0), and using 1.0 simplifies the calculations a bit. |
324 | | * |
325 | | * The second and third assumptions are standard and, in fact, define the |
326 | | * concept of "white point" and "black point" for the purposes of this color |
327 | | * space. These assumptions are, however, often either poorly documented or |
328 | | * not documented at all. At least the former is also not particularly intuitive: |
329 | | * in an additive color arrangement (such as a display), the color achieved by |
330 | | * full intensity on each of the primaries may be colored, and its color need |
331 | | * not correspond to any of the standard "color temperatures" usually used |
332 | | * as white points. |
333 | | * |
334 | | * The assumption is, unfortunately, critical to allow derivation of the |
335 | | * transformation from the primaries provided to the XYZ color space. If we |
336 | | * let {Xr, Yr, Zr}, {Xg, Yg, Z,}, and {Xb, Yb, Zb} denote the XYZ coordinates |
337 | | * of the red, green, and blue primaries, respectively, then the desired |
338 | | * conversion is: |
339 | | * |
340 | | * - - |
341 | | * {X, Y, Z} = {R, G, B} * | Xr Yr Zr | |
342 | | * | Xg Yg Zg | |
343 | | * | Xb Yb Zb | |
344 | | * - - |
345 | | * |
346 | | * The chromaticities of the primaries and the white point are derived by |
347 | | * adjusting the X, Y, and Z coordinates such that x + y + z = 1. Hence: |
348 | | * |
349 | | * x = X / (X + Y + Z) |
350 | | * |
351 | | * y = Y / (X + Y + Z) |
352 | | * |
353 | | * z = Z / (X + Y + Z) |
354 | | * |
355 | | * Note that these relationships preserve the ratios between components: |
356 | | * |
357 | | * x / y = X / Y |
358 | | * |
359 | | * Hence: |
360 | | * |
361 | | * X = (x / y) * Y |
362 | | * |
363 | | * Z = ((1 - (x + y)) / y) * Y |
364 | | * |
365 | | * Using this relationship, the conversion equation above can be restated as: |
366 | | * |
367 | | * - - |
368 | | * {X, Y, Z} = {R, G, B} * | Yr * xr / yr Yr Yr * zr / yr | |
369 | | * | Yg * xg / yg Yg Yg * zg / yg | |
370 | | * | Yb * xb / yb Yb Yb * zb / yb | |
371 | | * - - |
372 | | * |
373 | | * Where zr = 1.0 - (xr + yr), zg = 1.0 - (xg + yg), and zb = 1.0 - (xb + yb). |
374 | | * |
375 | | * As discussed above, in order to make the range parameters of the long form |
376 | | * Configure Image Data command meaningful, we must use the convention that |
377 | | * full intensity for all components is {1, 1, 1}, and no intensity is |
378 | | * {0, 0, 0}. Because the transformation is linear, the latter point provides |
379 | | * no information, but the former establishes the following relationship. |
380 | | * |
381 | | * - - |
382 | | * {Xw, Yw, Zw} = {1, 1, 1} * | Yr * xr / yr Yr Yr * zr / yr | |
383 | | * | Yg * xg / yg Yg Yg * zg / yg | |
384 | | * | Yb * xb / yb Yb Yb * zb / yb | |
385 | | * - - |
386 | | * |
387 | | * This is equivalent to: |
388 | | * |
389 | | * - - |
390 | | * {Xw, Yw, Zw} = {Yr, Yg, Yb} * | xr / yr 1.0 zr / yr | |
391 | | * | xg / yg 1.0 zg / yg | |
392 | | * | xb / yb 1.0 zb / yb | |
393 | | * - - |
394 | | * |
395 | | * = {Yr, Yg, Yb} * mtx |
396 | | * |
397 | | * Using the assumption that Yw = 1.0, we have Xw = xw / yw and Zw = zw / yw |
398 | | * (zw = 1 - (xw + yw)), so: |
399 | | * |
400 | | * {Yr, Yg, Yb} = {xw / yw, 1.0, zw / yw} * mtx^-1 |
401 | | * |
402 | | * Since Yr, Yg, and Yb are now known, it is possible to generate the |
403 | | * RGB ==> XYZ transformation. |
404 | | * |
405 | | * HP also provides for a gamma and gain parameter to be applied to each |
406 | | * primary, though it does not specify exactly what these mean. The |
407 | | * interpretation provided below (in the EncodeABC procedures) seems to |
408 | | * correspond with the observed phenomena, though it is not clear that this |
409 | | * interpretation is correct. Note also that the interpretation of gamma |
410 | | * requires that component intensities be positive. |
411 | | */ |
412 | | |
413 | | /* |
414 | | * The EncodeABC procedures for colorimetric RGB spaces. All three procedures |
415 | | * are the same, except for the array index used. |
416 | | */ |
417 | | #define colmet_DecodeABC_proc(procname, indx) \ |
418 | | static float \ |
419 | | procname( \ |
420 | | double val, \ |
421 | | const gs_cie_abc * pabc \ |
422 | | ) \ |
423 | 0 | { \ |
424 | 0 | const pcl_cs_client_data_t * pdata = \ |
425 | 0 | (const pcl_cs_client_data_t *) \ |
426 | 0 | pabc->common.client_data; \ |
427 | 0 | double inv_gamma = pdata->inv_gamma[indx]; \ |
428 | 0 | double inv_gain = pdata->inv_gain[indx]; \ |
429 | 0 | \ |
430 | 0 | convert_val( val, \ |
431 | 0 | pdata->min_val[indx], \ |
432 | 0 | pdata->range[indx], \ |
433 | 0 | pcl_lookup_tbl_get_tbl(pdata->plktbl1, indx) \ |
434 | 0 | ); \ |
435 | 0 | if (val < 0.0) \ |
436 | 0 | val = 0.0; \ |
437 | 0 | if (inv_gamma != 1.0) \ |
438 | 0 | val = pow(val, inv_gamma); \ |
439 | 0 | if (inv_gain != 1.0) \ |
440 | 0 | val = 1.0 - (1.0 - val) * inv_gain; \ |
441 | 0 | return val; \ |
442 | 0 | } Unexecuted instantiation: pccsbase.c:colmet_DecodeABC_0 Unexecuted instantiation: pccsbase.c:colmet_DecodeABC_1 Unexecuted instantiation: pccsbase.c:colmet_DecodeABC_2 |
443 | | |
444 | | colmet_DecodeABC_proc(colmet_DecodeABC_0, 0) |
445 | | colmet_DecodeABC_proc(colmet_DecodeABC_1, 1) |
446 | | colmet_DecodeABC_proc(colmet_DecodeABC_2, 2) |
447 | | |
448 | | static const gs_cie_abc_proc3 colmet_DecodeABC = { |
449 | | {colmet_DecodeABC_0, colmet_DecodeABC_1, colmet_DecodeABC_2} |
450 | | }; |
451 | | |
452 | | /* |
453 | | * Build the matrix to convert a calibrated RGB color space to XYZ space; see |
454 | | * the discussion above for the reasoning behind this derivation. |
455 | | * |
456 | | * Returns 0 on success, < 0 in the event of an error. |
457 | | */ |
458 | | static int |
459 | | build_colmet_conv_mtx(const pcl_cid_col_common_t * pdata, |
460 | | pcl_vec3_t * pwhite_pt, pcl_mtx3_t * pmtx) |
461 | 0 | { |
462 | 0 | pcl_vec3_t tmp_vec; |
463 | 0 | pcl_mtx3_t inv_mtx; |
464 | 0 | const float *pf = (float *)pdata->chroma; |
465 | 0 | int i, code; |
466 | |
|
467 | 0 | for (i = 0; i < 3; i++) { |
468 | 0 | double x = pf[2 * i]; |
469 | 0 | double y = pf[2 * i + 1]; |
470 | |
|
471 | 0 | pmtx->a[3 * i] = x / y; |
472 | 0 | pmtx->a[3 * i + 1] = 1.0; |
473 | 0 | pmtx->a[3 * i + 2] = (1.0 - x - y) / y; |
474 | 0 | } |
475 | 0 | if ((code = pcl_mtx3_invert(pmtx, &inv_mtx)) < 0) |
476 | 0 | return code; |
477 | | |
478 | 0 | pwhite_pt->vc.v1 = pdata->chroma[3].x / pdata->chroma[3].y; |
479 | 0 | pwhite_pt->vc.v2 = 1.0; |
480 | 0 | pwhite_pt->vc.v3 = (1.0 - pdata->chroma[3].x - pdata->chroma[3].y) |
481 | 0 | / pdata->chroma[3].y; |
482 | 0 | pcl_vec3_xform(pwhite_pt, &tmp_vec, &inv_mtx); |
483 | |
|
484 | 0 | for (i = 0; i < 9; i++) |
485 | 0 | pmtx->a[i] *= tmp_vec.va[i / 3]; |
486 | 0 | return 0; |
487 | 0 | } |
488 | | |
489 | | /* |
490 | | * Finish the creation of a colorimetric RGB color space. |
491 | | */ |
492 | | static int |
493 | | finish_colmet_cspace(gs_color_space * pcspace, const pcl_cid_data_t * pcid) |
494 | 0 | { |
495 | 0 | pcl_mtx3_t mtxABC; |
496 | 0 | pcl_vec3_t white_pt; |
497 | 0 | const pcl_cid_col_common_t *pcoldata; |
498 | 0 | int code = 0; |
499 | |
|
500 | 0 | if (pcid->len == 6) |
501 | 0 | pcoldata = cid_data_default[pcl_cspace_Colorimetric].pchroma; |
502 | 0 | else |
503 | 0 | pcoldata = &(pcid->u.col.colmet); |
504 | 0 | if ((code = build_colmet_conv_mtx(pcoldata, &white_pt, &mtxABC)) < 0) |
505 | 0 | return code; |
506 | | |
507 | | /* RangeABC has the default value */ |
508 | 0 | *(gs_cie_abc_DecodeABC(pcspace)) = colmet_DecodeABC; |
509 | 0 | pcl_mtx3_convert_to_gs(&mtxABC, gs_cie_abc_MatrixABC(pcspace)); |
510 | |
|
511 | 0 | gs_cie_RangeLMN(pcspace)->ranges[0].rmin = 0; |
512 | 0 | gs_cie_RangeLMN(pcspace)->ranges[0].rmax = white_pt.va[0]; |
513 | 0 | gs_cie_RangeLMN(pcspace)->ranges[1].rmin = 0; |
514 | 0 | gs_cie_RangeLMN(pcspace)->ranges[1].rmax = white_pt.va[1]; |
515 | 0 | gs_cie_RangeLMN(pcspace)->ranges[2].rmin = 0; |
516 | 0 | gs_cie_RangeLMN(pcspace)->ranges[2].rmax = white_pt.va[2]; |
517 | | /* DecodeLMN and MatrixLMN have the default values */ |
518 | |
|
519 | 0 | pcl_vec3_to_gs_vector3(gs_cie_WhitePoint(pcspace), white_pt); |
520 | | /* BlackPoint has the default value */ |
521 | |
|
522 | 0 | return 0; |
523 | 0 | } |
524 | | |
525 | | /* |
526 | | * The CIE L*a*b* case. |
527 | | * |
528 | | * The mapping from L*a*b* space to XYZ space is fairly simple over most of |
529 | | * its range, but becomes complicated in the range of dark grays because the |
530 | | * dominant cubic/cube root relationship changes to linear in this region. |
531 | | * |
532 | | * Let: |
533 | | * |
534 | | * f(h) = (h > (6/29)^3 ? h^(1/3) : (29^2 / (3 * 6^2)) * h + 4/29) |
535 | | * |
536 | | * g(h) = (h > (6/29)^3 ? 116 * h^(1/3) - 16 : (29/3)^3 * h) |
537 | | * |
538 | | * Note that, for h = (6/29)^3, the two different expressions for g(h) yield |
539 | | * the same result: |
540 | | * |
541 | | * 116 * h^(1.3) - 16 = (116 * 6 / 29) - 16 = 24 - 16 = 8, and |
542 | | * |
543 | | * (29/3)^3 * h = (29 * 6 / 3 * 29)^3 = 2^3 = 8 |
544 | | * |
545 | | * Since each part of g is monotonically increasing, g(h) is itself |
546 | | * monotonically increasing, and therefore invertible. Similarly, for |
547 | | * this value of h both expressions for f yield the same result: |
548 | | * |
549 | | * h^(1/3) = 6/29, and |
550 | | * |
551 | | * (29^2 / (3 * 6^2)) * h + 4/29 = (29^2 * 6^3) / (29^3 * 3 * 6^2) + 4/29 |
552 | | * |
553 | | * = 2/29 + 4/29 = 6/29 |
554 | | * |
555 | | * Again, the individual parts of f are monotonically increasing, hence f is |
556 | | * monotonically increasing and therefore invertible. |
557 | | * |
558 | | * Let { Xw, 1.0, Yw } be the desired white-point. Then, the conversion from |
559 | | * XYZ ==> L*a*b* is given by: |
560 | | * |
561 | | * L* = g(Y) |
562 | | * |
563 | | * a* = 500 * (f(X/Xw) - f(Y)) |
564 | | * |
565 | | * b* = 200 * (f(Y) - f(Z/Zw)) |
566 | | * |
567 | | * Inverting this relationship, we find that: |
568 | | * |
569 | | * Y = g^-1(L*) |
570 | | * |
571 | | * X = Xw * f^-1(a* / 500 + f(Y)) = Xw * f^-1(a* / 500 + f(g^-1(L*))) |
572 | | * |
573 | | * Z = Zw * f^-1(b* / 200 + f(Y)) = Zw * f^-1(f(g^-1(L*)) - b* / 200) |
574 | | * |
575 | | * Before providing expressions for f^-1 and g^-1 (we know from the argument |
576 | | * above that these functions exist), we should note that the structure of the |
577 | | * PostScript CIE color spaces cannot deal directly with a relationship such as |
578 | | * described above, because all cross-component steps (operations that depend |
579 | | * more than one component) must be linear. It is possible, however, to convert |
580 | | * the relationship to the required form by extracting the value of Y in two |
581 | | * steps. This is accomplished by the following algorithm: |
582 | | * |
583 | | * T1 = f(g^-1(L*)) |
584 | | * a1 = a* / 500 |
585 | | * b1 = b* / 200 |
586 | | * - - |
587 | | * { a2, T1, b2 } = { T1, a1, b1 } * | 1 1 1 | |
588 | | * | 1 0 0 | |
589 | | * | 0 0 -1 | |
590 | | * - - |
591 | | * X = Xw * f^-1(a2) |
592 | | * Y = f^-1(f(g^-1(L*))) |
593 | | * Z = Zw * f^-1(b2) |
594 | | * |
595 | | * While the handling of the L* ==> Y conversion in this algorithm may seem a |
596 | | * bit overly complex, it is perfectly legitimate and, as shown below, results |
597 | | * in a very simple expression. |
598 | | * |
599 | | * To complete the algorithm, expressions for f^-1 and g^-1 must be provided. |
600 | | * These are derived directly from the forward expressions: |
601 | | * |
602 | | * f^-1(h) = (h > 6/29 ? h^3 : (3 * 6^2) * (h - 4/29) / 29^2) |
603 | | * |
604 | | * g^-1(h) = (h > 8 ? ((h + 16) / 116)^3 : (3/29)^3 * h) |
605 | | * |
606 | | * Note that because both f and g change representations at the same point, |
607 | | * there is only a single representation of f(g^-1(h)) required. Specifically, |
608 | | * if h > 8, then |
609 | | * |
610 | | * g-1(h) = ((h + 16) / 116)^3 > (24 / 116)^3 = (6/29)^3 |
611 | | * |
612 | | * so |
613 | | * |
614 | | * f(g^-1(h)) = (g^-1(h))^(1/3) = (h + 16) / 116 |
615 | | * |
616 | | * while if h <= 8 |
617 | | * |
618 | | * g-1(h) = (3/29)^3 * h <= (6/29)^3 |
619 | | * |
620 | | * so |
621 | | * |
622 | | * f(g-1(h)) = (29^2 / (3 * 6^2)) * g^-1(h) + 4/29 |
623 | | * |
624 | | * = ((29^2 * 3^3) / (29^3 * 3 * 6^2)) * h + 4/29 |
625 | | * |
626 | | * = h/116 + 16/116 = (h + 16) / 116 |
627 | | * |
628 | | * This is the algorithm used below, with the Encode procedures also responsible |
629 | | * for implementing the color lookup tables (if present). |
630 | | */ |
631 | | |
632 | | /* |
633 | | * Unlike the other color spaces, the DecodeABC procedures for the CIE L*a*b* |
634 | | * color space have slightly different code for the different components. The |
635 | | * conv_code operand allows for this difference. |
636 | | */ |
637 | | #define lab_DecodeABC_proc(procname, indx, conv_code) \ |
638 | | static float \ |
639 | | procname( \ |
640 | | double val, \ |
641 | | const gs_cie_abc * pabc \ |
642 | | ) \ |
643 | 0 | { \ |
644 | 0 | const pcl_cs_client_data_t * pdata = \ |
645 | 0 | (const pcl_cs_client_data_t *)\ |
646 | 0 | pabc->common.client_data; \ |
647 | 0 | \ |
648 | 0 | convert_val( val, \ |
649 | 0 | pdata->min_val[indx], \ |
650 | 0 | pdata->range[indx], \ |
651 | 0 | pcl_lookup_tbl_get_tbl(pdata->plktbl1, indx) \ |
652 | 0 | ); \ |
653 | 0 | conv_code; \ |
654 | 0 | return val; \ |
655 | 0 | } Unexecuted instantiation: pccsbase.c:lab_DecodeABC_0 Unexecuted instantiation: pccsbase.c:lab_DecodeABC_1 Unexecuted instantiation: pccsbase.c:lab_DecodeABC_2 |
656 | | |
657 | | lab_DecodeABC_proc(lab_DecodeABC_0, 0, (val = (val + 16.0) / 116.0)) |
658 | | lab_DecodeABC_proc(lab_DecodeABC_1, 1, (val /= 500)) |
659 | | lab_DecodeABC_proc(lab_DecodeABC_2, 2, (val /= 200)) |
660 | | |
661 | | static const gs_cie_abc_proc3 lab_DecodeABC = { |
662 | | {lab_DecodeABC_0, lab_DecodeABC_1, lab_DecodeABC_2} |
663 | | }; |
664 | | |
665 | | static const gs_matrix3 lab_MatrixABC = { |
666 | | {1, 1, 1}, {1, 0, 0}, {0, 0, -1}, |
667 | | false |
668 | | }; |
669 | | |
670 | | /* |
671 | | * The DecodeLMN procedures for CIE L*a*b* color spaces are all identical |
672 | | * except for the index. The explicit use of the white point is overkill, |
673 | | * since we know this will always be the D65 white point with Y normalized |
674 | | * to 1.0, but it guards against future variations. |
675 | | */ |
676 | | #define lab_DecodeLMN_proc(procname, indx) \ |
677 | | static float \ |
678 | | procname( \ |
679 | | double val, \ |
680 | | const gs_cie_common * pcie \ |
681 | | ) \ |
682 | 0 | { \ |
683 | 0 | if (val > 6.0 / 29.0) \ |
684 | 0 | val = val * val * val; \ |
685 | 0 | else \ |
686 | 0 | val = 108 * (29.0 * val + 4) / (29.0 * 29.0 * 29.0); \ |
687 | 0 | val *= (&(pcie->points.WhitePoint.u))[indx]; \ |
688 | 0 | return val; \ |
689 | 0 | } Unexecuted instantiation: pccsbase.c:lab_DecodeLMN_0 Unexecuted instantiation: pccsbase.c:lab_DecodeLMN_1 Unexecuted instantiation: pccsbase.c:lab_DecodeLMN_2 |
690 | | |
691 | | lab_DecodeLMN_proc(lab_DecodeLMN_0, 0) |
692 | | lab_DecodeLMN_proc(lab_DecodeLMN_1, 1) |
693 | | lab_DecodeLMN_proc(lab_DecodeLMN_2, 2) |
694 | | |
695 | | static const gs_cie_common_proc3 lab_DecodeLMN = { |
696 | | {lab_DecodeLMN_0, lab_DecodeLMN_1, lab_DecodeLMN_2} |
697 | | }; |
698 | | |
699 | | static const gs_vector3 lab_WhitePoint = { .9504f, 1.0f, 1.0889f }; |
700 | | |
701 | | /* |
702 | | * Finish the creation of a CIE L*a*b* color space. |
703 | | */ |
704 | | static int |
705 | | finish_lab_cspace(gs_color_space * pcspace, const pcl_cid_data_t * pcid) |
706 | 0 | { |
707 | | /* RangeABC has the default value */ |
708 | 0 | *(gs_cie_abc_DecodeABC(pcspace)) = lab_DecodeABC; |
709 | 0 | *(gs_cie_abc_MatrixABC(pcspace)) = lab_MatrixABC; |
710 | | |
711 | | /* RangeLMN and MatrixLMN have the default values */ |
712 | 0 | *(gs_cie_DecodeLMN(pcspace)) = lab_DecodeLMN; |
713 | |
|
714 | 0 | gs_cie_WhitePoint(pcspace) = lab_WhitePoint; |
715 | | /* BlackPoint has the default value */ |
716 | 0 | return 0; |
717 | 0 | } |
718 | | |
719 | | /* |
720 | | * The luminance-chrominance color space |
721 | | * |
722 | | * As HP would have it, the matrix provided in the long-form luminance- |
723 | | * chrominance color space specification maps the calibrated RGB coordinates |
724 | | * to the coordinates of the source space. This is, of course, the inverse |
725 | | * of the transform that is useful: from the desired color space to calibrated |
726 | | * RGB. |
727 | | * |
728 | | * The rest of the handling of luminance-chrominance spaces is similar to |
729 | | * that for colorimetric RGB spaces. Note, however, that in this case the |
730 | | * RangeLMN is the default value (primary components must be clipped to |
731 | | * [0, 1]), and the DecodeLMN function must verify that its output is still |
732 | | * in this range. |
733 | | * |
734 | | * As commented upon elsewhere, HP allows multiple lookup tables to be attached |
735 | | * to the same color space, but does not clarify what this should do. The |
736 | | * lookup table for the device dependent color spaces is not a problem; this |
737 | | * is implemented as a transfer function. The situation with device independent |
738 | | * color spaces is not as clear. The choice made here is to allow two device |
739 | | * independent lookup tables to be applied to the luminance-chrominance color |
740 | | * space: the luminance-chrominance lookup table and the colorimetric RGB |
741 | | * lookup table. This does not match HP's behavior, but the latter does not |
742 | | * make any sense, so this should not be an issue. |
743 | | */ |
744 | | |
745 | | /* |
746 | | * The DecodeABC procedures for luminance-chrominance color space are simple. |
747 | | */ |
748 | | #define lumchrom_DecodeABC_proc(procname, indx) \ |
749 | | static float \ |
750 | | procname( \ |
751 | | double val, \ |
752 | | const gs_cie_abc * pabc \ |
753 | | ) \ |
754 | 0 | { \ |
755 | 0 | const pcl_cs_client_data_t * pdata = \ |
756 | 0 | (const pcl_cs_client_data_t *) \ |
757 | 0 | pabc->common.client_data; \ |
758 | 0 | \ |
759 | 0 | convert_val( val, \ |
760 | 0 | pdata->min_val[indx], \ |
761 | 0 | pdata->range[indx], \ |
762 | 0 | pcl_lookup_tbl_get_tbl(pdata->plktbl1, indx) \ |
763 | 0 | ); \ |
764 | 0 | return val; \ |
765 | 0 | } Unexecuted instantiation: pccsbase.c:lumchrom_DecodeABC_0 Unexecuted instantiation: pccsbase.c:lumchrom_DecodeABC_1 Unexecuted instantiation: pccsbase.c:lumchrom_DecodeABC_2 |
766 | | |
767 | | lumchrom_DecodeABC_proc(lumchrom_DecodeABC_0, 0) |
768 | | lumchrom_DecodeABC_proc(lumchrom_DecodeABC_1, 1) |
769 | | lumchrom_DecodeABC_proc(lumchrom_DecodeABC_2, 2) |
770 | | |
771 | | static const gs_cie_abc_proc3 lumchrom_DecodeABC = { |
772 | | {lumchrom_DecodeABC_0, lumchrom_DecodeABC_1, lumchrom_DecodeABC_2} |
773 | | }; |
774 | | |
775 | | /* |
776 | | * The DecodeLMN procedures for luminance-chrominance spaces are similar |
777 | | * to the colorimetric DecodeABC procedures. Since there is no Range* parameter |
778 | | * for the XYZ components, this procedure checks the range of its output. |
779 | | */ |
780 | | #define lumchrom_DecodeLMN_proc(procname, indx) \ |
781 | | static float \ |
782 | | procname( \ |
783 | | double val, \ |
784 | | const gs_cie_common * pcie \ |
785 | | ) \ |
786 | 0 | { \ |
787 | 0 | const pcl_cs_client_data_t * pdata = \ |
788 | 0 | (const pcl_cs_client_data_t *)\ |
789 | 0 | pcie->client_data; \ |
790 | 0 | double inv_gamma = pdata->inv_gamma[indx]; \ |
791 | 0 | double inv_gain = pdata->inv_gain[indx]; \ |
792 | 0 | \ |
793 | 0 | convert_val( val, \ |
794 | 0 | 0.0, \ |
795 | 0 | 1.0, \ |
796 | 0 | pcl_lookup_tbl_get_tbl(pdata->plktbl2, indx)); \ |
797 | 0 | if (inv_gamma != 1.0) \ |
798 | 0 | val = pow(val, inv_gamma); \ |
799 | 0 | if (inv_gain != 1.0) \ |
800 | 0 | val = 1.0 - (1.0 - val) * inv_gain; \ |
801 | 0 | if (val < 0.0) \ |
802 | 0 | val = 0.0; \ |
803 | 0 | else if (val > 1.0) \ |
804 | 0 | val = 1.0; \ |
805 | 0 | return val; \ |
806 | 0 | } Unexecuted instantiation: pccsbase.c:lumchrom_DecodeLMN_0 Unexecuted instantiation: pccsbase.c:lumchrom_DecodeLMN_1 Unexecuted instantiation: pccsbase.c:lumchrom_DecodeLMN_2 |
807 | | |
808 | | lumchrom_DecodeLMN_proc(lumchrom_DecodeLMN_0, 0) |
809 | | lumchrom_DecodeLMN_proc(lumchrom_DecodeLMN_1, 1) |
810 | | lumchrom_DecodeLMN_proc(lumchrom_DecodeLMN_2, 2) |
811 | | |
812 | | static const gs_cie_common_proc3 lumchrom_DecodeLMN = { |
813 | | {lumchrom_DecodeLMN_0, lumchrom_DecodeLMN_1, lumchrom_DecodeLMN_2} |
814 | | }; |
815 | | |
816 | | /* |
817 | | * Build the MatrixABC value for a luminance/chrominance color space. Note that |
818 | | * this is the inverse of the matrix provided in the Configure Image Data |
819 | | * command. |
820 | | * |
821 | | * Return 0 on success, < 0 in the event of an error. |
822 | | */ |
823 | | static int |
824 | | build_lum_chrom_mtxABC(const float pin_mtx[9], pcl_mtx3_t * pmtxABC) |
825 | 0 | { |
826 | 0 | int i; |
827 | 0 | pcl_mtx3_t tmp_mtx; |
828 | | |
829 | | /* transpose the input to create a row-order matrix */ |
830 | 0 | for (i = 0; i < 3; i++) { |
831 | 0 | int j; |
832 | |
|
833 | 0 | for (j = 0; j < 3; j++) |
834 | 0 | tmp_mtx.a[i * 3 + j] = pin_mtx[i + 3 * j]; |
835 | 0 | } |
836 | |
|
837 | 0 | return pcl_mtx3_invert(&tmp_mtx, pmtxABC); |
838 | 0 | } |
839 | | |
840 | | /* |
841 | | * Finish the creation of a luminance-chrominance color space. |
842 | | */ |
843 | | static int |
844 | | finish_lumchrom_cspace(gs_color_space * pcspace, const pcl_cid_data_t * pcid) |
845 | 0 | { |
846 | 0 | const float *pin_mtx; |
847 | 0 | pcl_mtx3_t mtxABC, mtxLMN; |
848 | 0 | pcl_vec3_t white_pt; |
849 | 0 | const pcl_cid_col_common_t *pcoldata; |
850 | 0 | int code = 0; |
851 | |
|
852 | 0 | if (pcid->len == 6) { |
853 | 0 | pcoldata = cid_data_default[pcl_cspace_LumChrom].pchroma; |
854 | 0 | pin_mtx = cid_data_default[pcl_cspace_LumChrom].pxform; |
855 | 0 | } else { |
856 | 0 | pcoldata = &(pcid->u.lum.colmet); |
857 | 0 | pin_mtx = pcid->u.lum.matrix; |
858 | 0 | } |
859 | |
|
860 | 0 | if (((code = build_lum_chrom_mtxABC(pin_mtx, &mtxABC)) < 0) || |
861 | 0 | ((code = build_colmet_conv_mtx(pcoldata, &white_pt, &mtxLMN)) < 0)) |
862 | 0 | return code; |
863 | | |
864 | | /* RangeABC has the default value */ |
865 | 0 | *(gs_cie_abc_DecodeABC(pcspace)) = lumchrom_DecodeABC; |
866 | 0 | pcl_mtx3_convert_to_gs(&mtxABC, gs_cie_abc_MatrixABC(pcspace)); |
867 | | |
868 | | /* RangeLMN has the default value */ |
869 | 0 | *(gs_cie_DecodeLMN(pcspace)) = lumchrom_DecodeLMN; |
870 | 0 | pcl_mtx3_convert_to_gs(&mtxLMN, gs_cie_MatrixLMN(pcspace)); |
871 | |
|
872 | 0 | pcl_vec3_to_gs_vector3(gs_cie_WhitePoint(pcspace), white_pt); |
873 | | /* BlackPoint has the default value */ |
874 | |
|
875 | 0 | return 0; |
876 | 0 | } |
877 | | |
878 | | static int (*const finish_cspace[(int)pcl_cspace_num]) (gs_color_space *, |
879 | | const pcl_cid_data_t |
880 | | *) = { |
881 | | 0, /* pcl_cspace_RGB */ |
882 | | 0, /* pcl_cspace_CMY */ |
883 | | finish_colmet_cspace, /* pcl_cspace_Colorimetric */ |
884 | | finish_lab_cspace, /* pcl_cspace_CIELab */ |
885 | | finish_lumchrom_cspace /* pcl_cspace_LumChrom */ |
886 | | }; |
887 | | |
888 | | /* |
889 | | * Free a PCL base color space. This decrements the reference count for the |
890 | | * GS color space, and frees any lookup tables that might have been |
891 | | * used (device independent color spaces only). |
892 | | */ |
893 | | static void |
894 | | free_base_cspace(gs_memory_t * pmem, void *pvbase, client_name_t cname) |
895 | 19.7k | { |
896 | 19.7k | pcl_cs_base_t *pbase = (pcl_cs_base_t *) pvbase; |
897 | | |
898 | 19.7k | rc_decrement(pbase->pcspace, "free_base_cspace"); |
899 | 19.7k | rc_decrement(pbase->client_data.plktbl1, "free_base_cspace"); |
900 | 19.7k | rc_decrement(pbase->client_data.plktbl2, "free_base_cspace"); |
901 | 19.7k | gs_free_object(pmem, pvbase, cname); |
902 | 19.7k | } |
903 | | |
904 | | /* |
905 | | * Allocate a PCL base color space. |
906 | | * |
907 | | * Because a PCL base color space and the associated graphic-library color |
908 | | * space must be kept in a one-to-one relationship, the latter color space is |
909 | | * allocated here as well. For this reason the PCL color space type is |
910 | | * an operand. |
911 | | * |
912 | | * Returns 0 on success, e_Memory in the event of a memory error. |
913 | | */ |
914 | | static int |
915 | | alloc_base_cspace(pcl_cs_base_t ** ppbase, |
916 | | pcl_cspace_type_t type, gs_memory_t * pmem) |
917 | 19.7k | { |
918 | 19.7k | pcl_cs_base_t *pbase = 0; |
919 | 19.7k | int code = 0; |
920 | | |
921 | 19.7k | *ppbase = 0; |
922 | 19.7k | rc_alloc_struct_1(pbase, |
923 | 19.7k | pcl_cs_base_t, |
924 | 19.7k | &st_cs_base_t, |
925 | 19.7k | pmem, return e_Memory, "allocate pcl base color space"); |
926 | 19.7k | pbase->rc.free = free_base_cspace; |
927 | 19.7k | pbase->type = type; |
928 | 19.7k | pbase->client_data.plktbl1 = 0; |
929 | 19.7k | pbase->client_data.plktbl2 = 0; |
930 | 19.7k | pbase->pcspace = 0; |
931 | | |
932 | 19.7k | if (type == pcl_cspace_White) |
933 | 7.25k | pbase->pcspace = gs_cspace_new_DeviceGray(pmem); |
934 | 12.5k | else if (type <= pcl_cspace_CMY) |
935 | 12.5k | pbase->pcspace = gs_cspace_new_DeviceRGB(pmem); |
936 | 0 | else |
937 | 0 | code = gs_cspace_build_CIEABC(&(pbase->pcspace), |
938 | 0 | &(pbase->client_data), pmem); |
939 | 19.7k | if (code < 0 || pbase->pcspace == NULL) |
940 | 0 | free_base_cspace(pmem, pbase, "allocate pcl base color space"); |
941 | 19.7k | else |
942 | 19.7k | *ppbase = pbase; |
943 | 19.7k | return code; |
944 | 19.7k | } |
945 | | |
946 | | /* |
947 | | * Create a unique instance of a pcl_cs_base_t object (if one does not already |
948 | | * exist). |
949 | | * |
950 | | * This code is not fully legitimate. To assure that a PCL base color space is |
951 | | * unique, it is also necessary to assure that the associated graphics library |
952 | | * color space is unique. Unfortunately, that is not a simple matter, because |
953 | | * graphic library color spaces are not themselves reference counted, though |
954 | | * they have reference-counted elements. |
955 | | * |
956 | | * We can get away with this arrangement for now by relying on a one-to-one |
957 | | * association between PCL base color spaces and the associated graphic library |
958 | | * color spaces. For all current implementations of the graphic library this |
959 | | * will work. The code may fail, however, for implementations that use a |
960 | | * "lazy evaluation" technique, as these may require access to the graphics |
961 | | * library color space after the PCL base color space has been released (the |
962 | | * graphic library color space will still be present in this case, but its |
963 | | * client data may have been changed). |
964 | | */ |
965 | | static int |
966 | | unshare_base_cspace(const gs_memory_t * mem, pcl_cs_base_t ** ppbase) |
967 | 0 | { |
968 | 0 | pcl_cs_base_t *pbase = *ppbase; |
969 | 0 | pcl_cs_base_t *pnew = 0; |
970 | 0 | int code; |
971 | | |
972 | | /* check if there is anything to do */ |
973 | 0 | if (pbase->rc.ref_count == 1) |
974 | 0 | return 0; |
975 | 0 | rc_decrement(pbase, "unshare PCL base color space"); |
976 | | |
977 | | /* allocate a new gs_color_space */ |
978 | 0 | if ((code = alloc_base_cspace(ppbase, pbase->type, pbase->rc.memory)) < 0) |
979 | 0 | return code; |
980 | 0 | pnew = *ppbase; |
981 | | |
982 | | /* copy the client data */ |
983 | 0 | init_client_data_from(&(pnew->client_data), &(pbase->client_data)); |
984 | | |
985 | | /* copy the color space (primarily for CIE color spaces; UGLY!!!) */ |
986 | 0 | if (pbase->type > pcl_cspace_CMY) { |
987 | 0 | gs_cie_abc *pcs1 = pbase->pcspace->params.abc; |
988 | 0 | gs_cie_abc *pcs2 = pnew->pcspace->params.abc; |
989 | 0 | pcs2->common.install_cspace = pcs1->common.install_cspace; |
990 | 0 | pcs2->common.RangeLMN = pcs1->common.RangeLMN; |
991 | 0 | pcs2->common.DecodeLMN = pcs1->common.DecodeLMN; |
992 | 0 | pcs2->common.MatrixLMN = pcs1->common.MatrixLMN; |
993 | 0 | pcs2->common.points = pcs1->common.points; |
994 | 0 | pcs2->RangeABC = pcs1->RangeABC; |
995 | 0 | pcs2->DecodeABC = pcs1->DecodeABC; |
996 | 0 | pcs2->MatrixABC = pcs1->MatrixABC; |
997 | 0 | } else |
998 | 0 | pnew->pcspace->params.pixel = pbase->pcspace->params.pixel; |
999 | 0 | return 0; |
1000 | 0 | } |
1001 | | |
1002 | | /* |
1003 | | * Build a PCL base color space. This should be invoked whenever a color space |
1004 | | * is required, typically after a Configure Image Data (CID) command. |
1005 | | * |
1006 | | * Returns 0 on success, < 0 in the event of an error. |
1007 | | */ |
1008 | | int |
1009 | | pcl_cs_base_build_cspace(pcl_cs_base_t ** ppbase, |
1010 | | const pcl_cid_data_t * pcid, gs_memory_t * pmem) |
1011 | 12.5k | { |
1012 | 12.5k | pcl_cs_base_t *pbase = *ppbase; |
1013 | 12.5k | pcl_cspace_type_t type = pcl_cid_get_cspace(pcid); |
1014 | 12.5k | int code = 0; |
1015 | | |
1016 | | /* release the existing color space, if present */ |
1017 | 12.5k | if (pbase != 0) { |
1018 | 0 | if_debug1m('c', pmem, "[c]releasing color space:%s\n", |
1019 | 0 | pcl_cid_cspace_get_debug_name(pmem, pbase->type)); |
1020 | 0 | rc_decrement(pbase, "build base pcl color space"); |
1021 | 0 | } |
1022 | | /* build basic structure and client info. structure */ |
1023 | 12.5k | if ((code = alloc_base_cspace(ppbase, type, pmem)) < 0) |
1024 | 0 | return code; |
1025 | 12.5k | pbase = *ppbase; |
1026 | 12.5k | build_client_data(&(pbase->client_data), pcid, pmem); |
1027 | | |
1028 | | /* fill in color space parameters */ |
1029 | 12.5k | if ((finish_cspace[type] != 0) && |
1030 | 0 | ((code = finish_cspace[type] (pbase->pcspace, pcid)) < 0)) |
1031 | 0 | free_base_cspace(pmem, pbase, "build base pcl color space"); |
1032 | 12.5k | return code; |
1033 | 12.5k | } |
1034 | | |
1035 | | /* |
1036 | | * Build a special base color space, used for setting the color white. |
1037 | | * This base space is unique in that it uses the DeviceGray graphic library |
1038 | | * color space. |
1039 | | * |
1040 | | * This routine is usually called once at initialization. |
1041 | | */ |
1042 | | int |
1043 | | pcl_cs_base_build_white_cspace(pcl_state_t * pcs, |
1044 | | pcl_cs_base_t ** ppbase, gs_memory_t * pmem) |
1045 | 29.9k | { |
1046 | 29.9k | int code = 0; |
1047 | | |
1048 | 29.9k | if (pcs->pwhite_cs == 0) |
1049 | 7.25k | code = alloc_base_cspace(&pcs->pwhite_cs, pcl_cspace_White, pmem); |
1050 | 29.9k | if (code >= 0) |
1051 | 29.9k | pcl_cs_base_copy_from(*ppbase, pcs->pwhite_cs); |
1052 | 29.9k | return code; |
1053 | 29.9k | } |
1054 | | |
1055 | | /* |
1056 | | * Update the lookup table information for a base color space. This applies |
1057 | | * only to device-independent color spaces (updating device dependent color |
1058 | | * spaces updates the transfer function in the current halftone). Passing a |
1059 | | * null pointer for the lookup table operand resets the tables for ALL color |
1060 | | * spaces to be the identity table. |
1061 | | * |
1062 | | * See the comments in pclookup.h for a description of how device independent |
1063 | | * lookup tables are interpreted in this implementation. |
1064 | | * |
1065 | | * Returns > 0 if the update changed the color space, 0 if the update did not |
1066 | | * change the color space, and < 0 in the event of an error. If the base color |
1067 | | * Space was updated, the current PCL indexed color space (which includes this |
1068 | | * color space as a base color space) must also be updated. |
1069 | | */ |
1070 | | int |
1071 | | pcl_cs_base_update_lookup_tbl(pcl_cs_base_t ** ppbase, |
1072 | | pcl_lookup_tbl_t * plktbl) |
1073 | 0 | { |
1074 | 0 | pcl_cs_base_t *pbase = *ppbase; |
1075 | 0 | pcl_lookup_tbl_t *plktbl1 = pbase->client_data.plktbl1; |
1076 | 0 | pcl_lookup_tbl_t *plktbl2 = pbase->client_data.plktbl2; |
1077 | 0 | int code = 0; |
1078 | |
|
1079 | 0 | if (plktbl == 0) { |
1080 | 0 | if ((pbase->client_data.plktbl1 == 0) && |
1081 | 0 | (pbase->client_data.plktbl2 == 0)) |
1082 | 0 | return 0; |
1083 | 0 | plktbl1 = 0; |
1084 | 0 | plktbl2 = 0; |
1085 | |
|
1086 | 0 | } else { |
1087 | 0 | pcl_cspace_type_t cstype = pbase->type; |
1088 | 0 | pcl_cspace_type_t lktype = pcl_lookup_tbl_get_cspace(plktbl); |
1089 | | |
1090 | | /* lookup tables for "higher" color spaces are always ignored */ |
1091 | 0 | if ((cstype < lktype) || |
1092 | 0 | (lktype == pcl_cspace_RGB) || (lktype == pcl_cspace_CMY)) |
1093 | 0 | return 0; |
1094 | | |
1095 | | /* CIE L*a*b* space and the L*a*b* lookup table must match */ |
1096 | 0 | if ((cstype == pcl_cspace_CIELab) || (lktype == pcl_cspace_CIELab)) { |
1097 | 0 | plktbl1 = plktbl; |
1098 | 0 | } else if (cstype == lktype) |
1099 | 0 | plktbl1 = plktbl; |
1100 | 0 | else |
1101 | 0 | plktbl2 = plktbl; |
1102 | 0 | } |
1103 | | |
1104 | | /* make a unique copy of the base color space */ |
1105 | 0 | if ((code = unshare_base_cspace(pbase->rc.memory, ppbase)) < 0) |
1106 | 0 | return code; |
1107 | 0 | pbase = *ppbase; |
1108 | | |
1109 | | /* update the lookup table information */ |
1110 | 0 | update_lookup_tbls(&(pbase->client_data), plktbl1, plktbl2); |
1111 | |
|
1112 | 0 | return 1; |
1113 | 0 | } |
1114 | | |
1115 | | /* |
1116 | | * Install a base color space into the graphic state. |
1117 | | * |
1118 | | * The pointer-pointer form of the first operand is for consistency with the |
1119 | | * other "install" procedures. |
1120 | | * |
1121 | | * Returns 0 on success, < 0 in the event of an error. |
1122 | | */ |
1123 | | int |
1124 | | pcl_cs_base_install(pcl_cs_base_t ** ppbase, pcl_state_t * pcs) |
1125 | 34.5k | { |
1126 | 34.5k | return gs_setcolorspace(pcs->pgs, (*ppbase)->pcspace); |
1127 | 34.5k | } |
1128 | | |
1129 | | /* |
1130 | | * One-time initialization routine. This exists only to handle possible non- |
1131 | | * initialization of BSS. |
1132 | | */ |
1133 | | void |
1134 | | pcl_cs_base_init(pcl_state_t * pcs) |
1135 | 8.97k | { |
1136 | 8.97k | pcs->pwhite_cs = 0; |
1137 | 8.97k | } |