xref: /plan9/sys/src/cmd/gs/src/gscolor2.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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 *)&params->base_space);
106     const float *pv = &params->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 *)&params->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