1 /* Copyright (C) 1992, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gscolor2.c,v 1.20 2004/04/08 07:59:19 igor Exp $ */
18 /* Level 2 color operators for Ghostscript library */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gxarith.h"
23 #include "gxfixed.h" /* ditto */
24 #include "gxmatrix.h" /* for gzstate.h */
25 #include "gxcspace.h" /* for gscolor2.h */
26 #include "gxcolor2.h"
27 #include "gzstate.h"
28 #include "gxpcolor.h"
29 #include "stream.h"
30
31 /* ---------------- General colors and color spaces ---------------- */
32
33 /* setcolorspace */
34 int
gs_setcolorspace(gs_state * pgs,const gs_color_space * pcs)35 gs_setcolorspace(gs_state * pgs, const gs_color_space * pcs)
36 {
37 int code = 0;
38 gs_color_space cs_old = *pgs->color_space;
39 gs_client_color cc_old = *pgs->ccolor;
40
41 if (pgs->in_cachedevice)
42 return_error(gs_error_undefined);
43
44 if (pcs->id != pgs->color_space->id) {
45 pcs->type->adjust_cspace_count(pcs, 1);
46 *pgs->color_space = *pcs;
47 if ( (code = pcs->type->install_cspace(pcs, pgs)) < 0 ||
48 (pgs->overprint && (code = gs_do_set_overprint(pgs)) < 0) ) {
49 *pgs->color_space = cs_old;
50 pcs->type->adjust_cspace_count(pcs, -1);
51 } else
52 cs_old.type->adjust_cspace_count(&cs_old, -1);
53 }
54
55 if (code >= 0) {
56 cs_full_init_color(pgs->ccolor, pcs);
57 cs_old.type->adjust_color_count(&cc_old, &cs_old, -1);
58 gx_unset_dev_color(pgs);
59 }
60
61 return code;
62 }
63
64 /* currentcolorspace */
65 const gs_color_space *
gs_currentcolorspace(const gs_state * pgs)66 gs_currentcolorspace(const gs_state * pgs)
67 {
68 return pgs->color_space;
69 }
70
71 /* setcolor */
72 int
gs_setcolor(gs_state * pgs,const gs_client_color * pcc)73 gs_setcolor(gs_state * pgs, const gs_client_color * pcc)
74 {
75 gs_color_space * pcs = pgs->color_space;
76 gs_client_color cc_old = *pgs->ccolor;
77
78 if (pgs->in_cachedevice)
79 return_error(gs_error_undefined); /* PLRM3 page 215. */
80 gx_unset_dev_color(pgs);
81 (*pcs->type->adjust_color_count)(pcc, pcs, 1);
82 *pgs->ccolor = *pcc;
83 (*pcs->type->restrict_color)(pgs->ccolor, pcs);
84 (*pcs->type->adjust_color_count)(&cc_old, pcs, -1);
85
86 return 0;
87 }
88
89 /* currentcolor */
90 const gs_client_color *
gs_currentcolor(const gs_state * pgs)91 gs_currentcolor(const gs_state * pgs)
92 {
93 return pgs->ccolor;
94 }
95
96 /* ------ Internal procedures ------ */
97
98 /* GC descriptors */
99 private_st_indexed_map();
100
101 /* Define a lookup_index procedure that just returns the map values. */
102 int
lookup_indexed_map(const gs_indexed_params * params,int index,float * values)103 lookup_indexed_map(const gs_indexed_params * params, int index, float *values)
104 {
105 int m = cs_num_components((const gs_color_space *)¶ms->base_space);
106 const float *pv = ¶ms->lookup.map->values[index * m];
107
108 memcpy(values, pv, sizeof(*values) * m);
109 return 0;
110 }
111
112 /* Free an indexed map and its values when the reference count goes to 0. */
113 void
free_indexed_map(gs_memory_t * pmem,void * pmap,client_name_t cname)114 free_indexed_map(gs_memory_t * pmem, void *pmap, client_name_t cname)
115 {
116 gs_free_object(pmem, ((gs_indexed_map *) pmap)->values, cname);
117 gs_free_object(pmem, pmap, cname);
118 }
119
120 /*
121 * Allocate an indexed map for an Indexed or Separation color space.
122 */
123 int
alloc_indexed_map(gs_indexed_map ** ppmap,int nvals,gs_memory_t * pmem,client_name_t cname)124 alloc_indexed_map(gs_indexed_map ** ppmap, int nvals, gs_memory_t * pmem,
125 client_name_t cname)
126 {
127 gs_indexed_map *pimap;
128
129 rc_alloc_struct_1(pimap, gs_indexed_map, &st_indexed_map, pmem,
130 return_error(gs_error_VMerror), cname);
131 if (nvals > 0) {
132 pimap->values =
133 (float *)gs_alloc_byte_array(pmem, nvals, sizeof(float), cname);
134
135 if (pimap->values == 0) {
136 gs_free_object(pmem, pimap, cname);
137 return_error(gs_error_VMerror);
138 }
139 } else
140 pimap->values = 0;
141 pimap->rc.free = free_indexed_map;
142 pimap->proc_data = 0; /* for GC */
143 pimap->num_values = nvals;
144 *ppmap = pimap;
145 return 0;
146 }
147
148 /* ---------------- Indexed color spaces ---------------- */
149
150 gs_private_st_composite(st_color_space_Indexed, gs_paint_color_space,
151 "gs_color_space_Indexed", cs_Indexed_enum_ptrs, cs_Indexed_reloc_ptrs);
152
153 /* ------ Color space ------ */
154
155 /* Define the Indexed color space type. */
156 private cs_proc_base_space(gx_base_space_Indexed);
157 private cs_proc_restrict_color(gx_restrict_Indexed);
158 private cs_proc_concrete_space(gx_concrete_space_Indexed);
159 private cs_proc_concretize_color(gx_concretize_Indexed);
160 private cs_proc_install_cspace(gx_install_Indexed);
161 private cs_proc_set_overprint(gx_set_overprint_Indexed);
162 private cs_proc_adjust_cspace_count(gx_adjust_cspace_Indexed);
163 private cs_proc_serialize(gx_serialize_Indexed);
164 const gs_color_space_type gs_color_space_type_Indexed = {
165 gs_color_space_index_Indexed, false, false,
166 &st_color_space_Indexed, gx_num_components_1,
167 gx_base_space_Indexed,
168 gx_init_paint_1, gx_restrict_Indexed,
169 gx_concrete_space_Indexed,
170 gx_concretize_Indexed, NULL,
171 gx_default_remap_color, gx_install_Indexed,
172 gx_set_overprint_Indexed,
173 gx_adjust_cspace_Indexed, gx_no_adjust_color_count,
174 gx_serialize_Indexed,
175 gx_cspace_is_linear_default
176 };
177
178 /* GC procedures. */
179
180 private uint
indexed_table_size(const gs_color_space * pcs)181 indexed_table_size(const gs_color_space *pcs)
182 {
183 return (pcs->params.indexed.hival + 1) *
184 cs_num_components((const gs_color_space *)
185 &pcs->params.indexed.base_space);
186
187 }
188 private
ENUM_PTRS_WITH(cs_Indexed_enum_ptrs,gs_color_space * pcs)189 ENUM_PTRS_WITH(cs_Indexed_enum_ptrs, gs_color_space *pcs)
190 {
191 return ENUM_USING(*pcs->params.indexed.base_space.type->stype,
192 &pcs->params.indexed.base_space,
193 sizeof(pcs->params.indexed.base_space), index - 1);
194 }
195 case 0:
196 if (pcs->params.indexed.use_proc)
197 ENUM_RETURN((void *)pcs->params.indexed.lookup.map);
198 else
199 return ENUM_CONST_STRING2(pcs->params.indexed.lookup.table.data,
200 indexed_table_size(pcs));
201 ENUM_PTRS_END
RELOC_PTRS_WITH(cs_Indexed_reloc_ptrs,gs_color_space * pcs)202 private RELOC_PTRS_WITH(cs_Indexed_reloc_ptrs, gs_color_space *pcs)
203 {
204 RELOC_USING(*pcs->params.indexed.base_space.type->stype,
205 &pcs->params.indexed.base_space,
206 sizeof(gs_base_color_space));
207 if (pcs->params.indexed.use_proc)
208 RELOC_PTR(gs_color_space, params.indexed.lookup.map);
209 else {
210 gs_const_string table;
211
212 table.data = pcs->params.indexed.lookup.table.data;
213 table.size = indexed_table_size(pcs);
214 RELOC_CONST_STRING_VAR(table);
215 pcs->params.indexed.lookup.table.data = table.data;
216 }
217 }
218 RELOC_PTRS_END
219
220 /* Return the base space of an Indexed color space. */
221 private const gs_color_space *
gx_base_space_Indexed(const gs_color_space * pcs)222 gx_base_space_Indexed(const gs_color_space * pcs)
223 {
224 return (const gs_color_space *)&(pcs->params.indexed.base_space);
225 }
226
227
228 /* Color space installation ditto. */
229
230 private int
gx_install_Indexed(const gs_color_space * pcs,gs_state * pgs)231 gx_install_Indexed(const gs_color_space * pcs, gs_state * pgs)
232 {
233 return (*pcs->params.indexed.base_space.type->install_cspace)
234 ((const gs_color_space *) & pcs->params.indexed.base_space, pgs);
235 }
236
237 /* Color space overprint setting ditto. */
238
239 private int
gx_set_overprint_Indexed(const gs_color_space * pcs,gs_state * pgs)240 gx_set_overprint_Indexed(const gs_color_space * pcs, gs_state * pgs)
241 {
242 return (*pcs->params.indexed.base_space.type->set_overprint)
243 ((const gs_color_space *)&pcs->params.indexed.base_space, pgs);
244 }
245
246 /* Color space reference count adjustment ditto. */
247
248 private void
gx_adjust_cspace_Indexed(const gs_color_space * pcs,int delta)249 gx_adjust_cspace_Indexed(const gs_color_space * pcs, int delta)
250 {
251 if (pcs->params.indexed.use_proc) {
252 rc_adjust_const(pcs->params.indexed.lookup.map, delta,
253 "gx_adjust_Indexed");
254 }
255 (*pcs->params.indexed.base_space.type->adjust_cspace_count)
256 ((const gs_color_space *)&pcs->params.indexed.base_space, delta);
257 }
258
259 /*
260 * Default palette mapping functions for indexed color maps. These just
261 * return the values already in the palette.
262 *
263 * For performance reasons, we provide four functions: special cases for 1,
264 * 3, and 4 entry palettes, and a general case. Note that these procedures
265 * do not range-check their input values.
266 */
267 private int
map_palette_entry_1(const gs_indexed_params * params,int indx,float * values)268 map_palette_entry_1(const gs_indexed_params * params, int indx, float *values)
269 {
270 values[0] = params->lookup.map->values[indx];
271 return 0;
272 }
273
274 private int
map_palette_entry_3(const gs_indexed_params * params,int indx,float * values)275 map_palette_entry_3(const gs_indexed_params * params, int indx, float *values)
276 {
277 const float *pv = &(params->lookup.map->values[3 * indx]);
278
279 values[0] = pv[0];
280 values[1] = pv[1];
281 values[2] = pv[2];
282 return 0;
283 }
284
285 private int
map_palette_entry_4(const gs_indexed_params * params,int indx,float * values)286 map_palette_entry_4(const gs_indexed_params * params, int indx, float *values)
287 {
288 const float *pv = &(params->lookup.map->values[4 * indx]);
289
290 values[0] = pv[0];
291 values[1] = pv[1];
292 values[2] = pv[2];
293 values[3] = pv[3];
294 return 0;
295 }
296
297 private int
map_palette_entry_n(const gs_indexed_params * params,int indx,float * values)298 map_palette_entry_n(const gs_indexed_params * params, int indx, float *values)
299 {
300 int m = cs_num_components((const gs_color_space *)¶ms->base_space);
301
302 memcpy((void *)values,
303 (const void *)(params->lookup.map->values + indx * m),
304 m * sizeof(float)
305 );
306
307 return 0;
308 }
309
310 /*
311 * Allocate an indexed map to be used as a palette for indexed color space.
312 */
313 private gs_indexed_map *
alloc_indexed_palette(const gs_color_space * pbase_cspace,int nvals,gs_memory_t * pmem)314 alloc_indexed_palette(
315 const gs_color_space * pbase_cspace,
316 int nvals,
317 gs_memory_t * pmem
318 )
319 {
320 int num_comps = gs_color_space_num_components(pbase_cspace);
321 gs_indexed_map *pimap;
322 int code =
323 alloc_indexed_map(&pimap, nvals * num_comps, pmem,
324 "alloc_indexed_palette");
325
326 if (code < 0)
327 return 0;
328 if (num_comps == 1)
329 pimap->proc.lookup_index = map_palette_entry_1;
330 else if (num_comps == 3)
331 pimap->proc.lookup_index = map_palette_entry_3;
332 else if (num_comps == 4)
333 pimap->proc.lookup_index = map_palette_entry_4;
334 else
335 pimap->proc.lookup_index = map_palette_entry_n;
336 return pimap;
337 }
338
339 /*
340 * Build an indexed color space.
341 */
342 int
gs_cspace_build_Indexed(gs_color_space ** ppcspace,const gs_color_space * pbase_cspace,uint num_entries,const gs_const_string * ptbl,gs_memory_t * pmem)343 gs_cspace_build_Indexed(
344 gs_color_space ** ppcspace,
345 const gs_color_space * pbase_cspace,
346 uint num_entries,
347 const gs_const_string * ptbl,
348 gs_memory_t * pmem
349 )
350 {
351 gs_color_space *pcspace = 0;
352 gs_indexed_params *pindexed = 0;
353 int code;
354
355 if ((pbase_cspace == 0) || !pbase_cspace->type->can_be_base_space)
356 return_error(gs_error_rangecheck);
357
358 code = gs_cspace_alloc(&pcspace, &gs_color_space_type_Indexed, pmem);
359 if (code < 0)
360 return code;
361 pindexed = &(pcspace->params.indexed);
362 if (ptbl == 0) {
363 pindexed->lookup.map =
364 alloc_indexed_palette(pbase_cspace, num_entries, pmem);
365 if (pindexed->lookup.map == 0) {
366 gs_free_object(pmem, pcspace, "gs_cspace_build_Indexed");
367 return_error(gs_error_VMerror);
368 }
369 pindexed->use_proc = true;
370 } else {
371 pindexed->lookup.table = *ptbl;
372 pindexed->use_proc = false;
373 }
374 gs_cspace_init_from((gs_color_space *) & (pindexed->base_space),
375 pbase_cspace);
376 pindexed->hival = num_entries - 1;
377 *ppcspace = pcspace;
378 return 0;
379 }
380
381 /*
382 * Return the number of entries in an indexed color space.
383 */
384 int
gs_cspace_indexed_num_entries(const gs_color_space * pcspace)385 gs_cspace_indexed_num_entries(const gs_color_space * pcspace)
386 {
387 if (gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed)
388 return 0;
389 return pcspace->params.indexed.hival + 1;
390 }
391
392 /*
393 * Get the palette for an indexed color space. This will return a null
394 * pointer if the color space is not an indexed color space or if the
395 * color space does not use the mapped index palette.
396 */
397 float *
gs_cspace_indexed_value_array(const gs_color_space * pcspace)398 gs_cspace_indexed_value_array(const gs_color_space * pcspace)
399 {
400 if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
401 pcspace->params.indexed.use_proc
402 )
403 return 0;
404 return pcspace->params.indexed.lookup.map->values;
405 }
406
407 /*
408 * Set the lookup procedure to be used with an indexed color space.
409 */
410 int
gs_cspace_indexed_set_proc(gs_color_space * pcspace,int (* proc)(const gs_indexed_params *,int,float *))411 gs_cspace_indexed_set_proc(
412 gs_color_space * pcspace,
413 int (*proc)(const gs_indexed_params *, int, float *)
414 )
415 {
416 if ((gs_color_space_get_index(pcspace) != gs_color_space_index_Indexed) ||
417 !pcspace->params.indexed.use_proc
418 )
419 return_error(gs_error_rangecheck);
420 pcspace->params.indexed.lookup.map->proc.lookup_index = proc;
421 return 0;
422 }
423
424 /* ------ Colors ------ */
425
426 /* Force an Indexed color into legal range. */
427 private void
gx_restrict_Indexed(gs_client_color * pcc,const gs_color_space * pcs)428 gx_restrict_Indexed(gs_client_color * pcc, const gs_color_space * pcs)
429 {
430 float value = pcc->paint.values[0];
431
432 pcc->paint.values[0] =
433 (is_fneg(value) ? 0 :
434 value >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
435 value);
436 }
437
438 /* Color remapping for Indexed color spaces. */
439 private const gs_color_space *
gx_concrete_space_Indexed(const gs_color_space * pcs,const gs_imager_state * pis)440 gx_concrete_space_Indexed(const gs_color_space * pcs,
441 const gs_imager_state * pis)
442 {
443 const gs_color_space *pbcs =
444 (const gs_color_space *)&pcs->params.indexed.base_space;
445
446 return cs_concrete_space(pbcs, pis);
447 }
448
449 private int
gx_concretize_Indexed(const gs_client_color * pc,const gs_color_space * pcs,frac * pconc,const gs_imager_state * pis)450 gx_concretize_Indexed(const gs_client_color * pc, const gs_color_space * pcs,
451 frac * pconc, const gs_imager_state * pis)
452 {
453 float value = pc->paint.values[0];
454 int index =
455 (is_fneg(value) ? 0 :
456 value >= pcs->params.indexed.hival ? pcs->params.indexed.hival :
457 (int)value);
458 const gs_color_space *pbcs =
459 (const gs_color_space *)&pcs->params.indexed.base_space;
460 gs_client_color cc;
461 int code = gs_cspace_indexed_lookup(&pcs->params.indexed, index, &cc);
462
463 if (code < 0)
464 return code;
465 return (*pbcs->type->concretize_color) (&cc, pbcs, pconc, pis);
466 }
467
468 /* Look up an index in an Indexed color space. */
469 int
gs_cspace_indexed_lookup(const gs_indexed_params * pip,int index,gs_client_color * pcc)470 gs_cspace_indexed_lookup(const gs_indexed_params *pip, int index,
471 gs_client_color *pcc)
472 {
473 if (pip->use_proc) {
474 return pip->lookup.map->proc.lookup_index
475 (pip, index, &pcc->paint.values[0]);
476 } else {
477 const gs_color_space *pbcs = (const gs_color_space *)&pip->base_space;
478 int m = cs_num_components(pbcs);
479 const byte *pcomp = pip->lookup.table.data + m * index;
480
481 switch (m) {
482 default: { /* DeviceN */
483 int i;
484
485 for (i = 0; i < m; ++i)
486 pcc->paint.values[i] = pcomp[i] * (1.0 / 255.0);
487 }
488 break;
489 case 4:
490 pcc->paint.values[3] = pcomp[3] * (1.0 / 255.0);
491 case 3:
492 pcc->paint.values[2] = pcomp[2] * (1.0 / 255.0);
493 case 2:
494 pcc->paint.values[1] = pcomp[1] * (1.0 / 255.0);
495 case 1:
496 pcc->paint.values[0] = pcomp[0] * (1.0 / 255.0);
497 }
498 return 0;
499 }
500 }
501
502 /* ---------------- Serialization. -------------------------------- */
503
504 private int
gx_serialize_Indexed(const gs_color_space * pcs,stream * s)505 gx_serialize_Indexed(const gs_color_space * pcs, stream * s)
506 {
507 const gs_indexed_params * p = &pcs->params.indexed;
508 uint n;
509 int code = gx_serialize_cspace_type(pcs, s);
510
511 if (code < 0)
512 return code;
513 code = cs_serialize((const gs_color_space *)&p->base_space, s);
514 if (code < 0)
515 return code;
516 code = sputs(s, (const byte *)&p->hival, sizeof(p->hival), &n);
517 if (code < 0)
518 return code;
519 code = sputs(s, (const byte *)&p->use_proc, sizeof(p->use_proc), &n);
520 if (code < 0)
521 return code;
522 if (p->use_proc) {
523 code = sputs(s, (const byte *)&p->lookup.map->num_values,
524 sizeof(p->lookup.map->num_values), &n);
525 if (code < 0)
526 return code;
527 code = sputs(s, (const byte *)&p->lookup.map->values[0],
528 sizeof(p->lookup.map->values[0]) * p->lookup.map->num_values, &n);
529 } else {
530 code = sputs(s, (const byte *)&p->lookup.table.size,
531 sizeof(p->lookup.table.size), &n);
532 if (code < 0)
533 return code;
534 code = sputs(s, p->lookup.table.data, p->lookup.table.size, &n);
535 }
536 return code;
537 }
538
539 /* ---------------- High level device support -------------------------------- */
540
541 /*
542 * This special function forces a device to include the current
543 * color space into the output. Returns 'rangecheck' if the device can't handle it.
544 * The primary reason is to include DefaultGray, DefaultRGB, DefaultCMYK into PDF.
545 * Should be called for each page that requires the resource.
546 * Redundant calls per page with same cspace id are allowed.
547 * Redundant calls per page with different cspace id are are allowed but
548 * highly undesirable.
549 * No need to call it with color spaces explicitly referred by the document,
550 * because they are included automatically.
551 * res_name and name_length passes the resource name.
552 */
553 int
gs_includecolorspace(gs_state * pgs,const byte * res_name,int name_length)554 gs_includecolorspace(gs_state * pgs, const byte *res_name, int name_length)
555 {
556 return (*dev_proc(pgs->device, include_color_space))(pgs->device, pgs->color_space, res_name, name_length);
557 }
558