xref: /plan9/sys/src/cmd/gs/src/gshtx.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 1999 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: gshtx.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
18 /* Stand-alone halftone/transfer function related code */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gsutil.h"		/* for gs_next_ids */
24 #include "gxfmap.h"
25 #include "gzstate.h"
26 #include "gzht.h"
27 #include "gshtx.h"		/* must come after g*ht.h */
28 
29 /*
30  * Procedure to free the set of components when a halftone is released.
31  */
32 private void
free_comps(gs_memory_t * pmem,void * pvht,client_name_t cname)33 free_comps(
34 	      gs_memory_t * pmem,
35 	      void *pvht,
36 	      client_name_t cname
37 )
38 {
39     gs_ht *pht = (gs_ht *) pvht;
40 
41     gs_free_object(pmem, pht->params.ht_multiple.components, cname);
42     gs_free_object(pmem, pvht, cname);
43 }
44 
45 /*
46  * Stub transfer function, to be applied to components that are not provided
47  * with a transfer function.
48  */
49 private float
null_closure_transfer(floatp val,const gx_transfer_map * pmap_dummy,const void * dummy)50 null_closure_transfer(
51 			 floatp val,
52 			 const gx_transfer_map * pmap_dummy,	/* NOTUSED */
53 			 const void *dummy	/* NOTUSED */
54 )
55 {
56     return val;
57 }
58 
59 
60 /*
61  * Build a gs_ht halftone structure.
62  */
63 int
gs_ht_build(gs_ht ** ppht,uint num_comps,gs_memory_t * pmem)64 gs_ht_build(
65 	       gs_ht ** ppht,
66 	       uint num_comps,
67 	       gs_memory_t * pmem
68 )
69 {
70     gs_ht *pht;
71     gs_ht_component *phtc;
72     int i;
73 
74     /* must have at least one component */
75     *ppht = 0;
76     if (num_comps == 0)
77 	return_error(gs_error_rangecheck);
78 
79     /* allocate the halftone and the array of components */
80     rc_alloc_struct_1(pht,
81 		      gs_ht,
82 		      &st_gs_ht,
83 		      pmem,
84 		      return_error(gs_error_VMerror),
85 		      "gs_ht_build"
86 	);
87     phtc = gs_alloc_struct_array(pmem,
88 				 num_comps,
89 				 gs_ht_component,
90 				 &st_ht_comp_element,
91 				 "gs_ht_build"
92 	);
93     if (phtc == 0) {
94 	gs_free_object(pmem, pht, "gs_ht_build");
95 	return_error(gs_error_VMerror);
96     }
97     /* initialize the halftone */
98     pht->type = ht_type_multiple;
99     pht->rc.free = free_comps;
100     pht->params.ht_multiple.components = phtc;
101     pht->params.ht_multiple.num_comp = num_comps;
102 
103     for (i = 0; i < num_comps; i++) {
104         phtc[i].comp_number = i;
105 	phtc[i].cname = 0;
106 	phtc[i].type = ht_type_none;
107     }
108 
109     *ppht = pht;
110 
111     return 0;
112 }
113 
114 /*
115  * Set a spot-function halftone component in a gs_ht halftone.
116  */
117 int
gs_ht_set_spot_comp(gs_ht * pht,int comp,floatp freq,floatp angle,float (* spot_func)(floatp,floatp),bool accurate,gs_ht_transfer_proc transfer,const void * client_data)118 gs_ht_set_spot_comp(
119 		       gs_ht * pht,
120 		       int comp,
121 		       floatp freq,
122 		       floatp angle,
123 		       float (*spot_func) (floatp, floatp),
124 		       bool accurate,
125 		       gs_ht_transfer_proc transfer,
126 		       const void *client_data
127 )
128 {
129     gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]);
130 
131     if (comp >= pht->params.ht_multiple.num_comp)
132 	return_error(gs_error_rangecheck);
133     if (phtc->type != ht_type_none)
134 	return_error(gs_error_invalidaccess);
135 
136     phtc->type = ht_type_spot;
137     phtc->params.ht_spot.screen.frequency = freq;
138     phtc->params.ht_spot.screen.angle = angle;
139     phtc->params.ht_spot.screen.spot_function = spot_func;
140     phtc->params.ht_spot.accurate_screens = accurate;
141     phtc->params.ht_spot.transfer = gs_mapped_transfer;
142 
143     phtc->params.ht_spot.transfer_closure.proc =
144 	(transfer == 0 ? null_closure_transfer : transfer);
145     phtc->params.ht_spot.transfer_closure.data = client_data;
146 
147     return 0;
148 }
149 
150 /*
151  * Set a threshold halftone component in a gs_ht halftone. Note that the
152  * caller is responsible for releasing the threshold data.
153  */
154 int
gs_ht_set_threshold_comp(gs_ht * pht,int comp,int width,int height,const gs_const_string * thresholds,gs_ht_transfer_proc transfer,const void * client_data)155 gs_ht_set_threshold_comp(
156 			    gs_ht * pht,
157 			    int comp,
158 			    int width,
159 			    int height,
160 			    const gs_const_string * thresholds,
161 			    gs_ht_transfer_proc transfer,
162 			    const void *client_data
163 )
164 {
165     gs_ht_component *phtc = &(pht->params.ht_multiple.components[comp]);
166 
167     if (comp >= pht->params.ht_multiple.num_comp)
168 	return_error(gs_error_rangecheck);
169     if (phtc->type != ht_type_none)
170 	return_error(gs_error_invalidaccess);
171 
172     phtc->type = ht_type_threshold;
173     phtc->params.ht_threshold.width = width;
174     phtc->params.ht_threshold.height = height;
175     phtc->params.ht_threshold.thresholds = *thresholds;
176     phtc->params.ht_threshold.transfer = gs_mapped_transfer;
177 
178     phtc->params.ht_threshold.transfer_closure.proc =
179 	(transfer == 0 ? null_closure_transfer : transfer);
180     phtc->params.ht_threshold.transfer_closure.data = client_data;
181 
182     return 0;
183 }
184 
185 /*
186  * Increase the reference count of a gs_ht structure by 1.
187  */
188 void
gs_ht_reference(gs_ht * pht)189 gs_ht_reference(
190 		   gs_ht * pht
191 )
192 {
193     rc_increment(pht);
194 }
195 
196 /*
197  * Decrement the reference count of a gs_ht structure by 1. Free the
198  * structure if the reference count reaches 0.
199  */
200 void
gs_ht_release(gs_ht * pht)201 gs_ht_release(
202 		 gs_ht * pht
203 )
204 {
205     rc_decrement_only(pht, "gs_ht_release");
206 }
207 
208 
209 /*
210  *  Verify that a gs_ht halftone is legitimate.
211  */
212 private int
check_ht(gs_ht * pht)213 check_ht(
214 	    gs_ht * pht
215 )
216 {
217     int i;
218     int num_comps = pht->params.ht_multiple.num_comp;
219 
220     if (pht->type != ht_type_multiple)
221 	return_error(gs_error_unregistered);
222     for (i = 0; i < num_comps; i++) {
223 	gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]);
224 	if ((phtc->type != ht_type_spot) && (phtc->type != ht_type_threshold))
225 	    return_error(gs_error_unregistered);
226     }
227     return 0;
228 }
229 
230 /*
231  *  Load a transfer map from a gs_ht_transfer_proc function.
232  */
233 private void
build_transfer_map(gs_ht_component * phtc,gx_transfer_map * pmap)234 build_transfer_map(
235 		      gs_ht_component * phtc,
236 		      gx_transfer_map * pmap
237 )
238 {
239     gs_ht_transfer_proc proc;
240     const void *client_info;
241     int i;
242     frac *values = pmap->values;
243 
244     if (phtc->type == ht_type_spot) {
245 	proc = phtc->params.ht_spot.transfer_closure.proc;
246 	client_info = phtc->params.ht_spot.transfer_closure.data;
247     } else {
248 	proc = phtc->params.ht_threshold.transfer_closure.proc;
249 	client_info = phtc->params.ht_threshold.transfer_closure.data;
250     }
251 
252     for (i = 0; i < transfer_map_size; i++) {
253 	float fval =
254 	    proc(i * (1 / (double)(transfer_map_size - 1)), pmap, client_info);
255 
256 	values[i] =
257 	    (fval <= 0.0 ? frac_0 : fval >= 1.0 ? frac_1 :
258 	     float2frac(fval));
259     }
260 }
261 
262 /*
263  *  Allocate the order and transfer maps required by a halftone, and perform
264  *  some elementary initialization. This will also build the component index
265  *  to order index map.
266  */
267 private gx_ht_order_component *
alloc_ht_order(const gs_ht * pht,gs_memory_t * pmem,byte * comp2order)268 alloc_ht_order(
269 		  const gs_ht * pht,
270 		  gs_memory_t * pmem,
271 		  byte * comp2order
272 )
273 {
274     int num_comps = pht->params.ht_multiple.num_comp;
275     gx_ht_order_component *pocs = gs_alloc_struct_array(
276 							   pmem,
277 					   pht->params.ht_multiple.num_comp,
278 						      gx_ht_order_component,
279 					     &st_ht_order_component_element,
280 							   "alloc_ht_order"
281     );
282     int inext = 0;
283     int i;
284 
285     if (pocs == 0)
286 	return 0;
287     pocs->corder.transfer = 0;
288 
289     for (i = 0; i < num_comps; i++) {
290 	gs_ht_component *phtc = &(pht->params.ht_multiple.components[i]);
291 	gx_transfer_map *pmap = gs_alloc_struct(pmem,
292 						gx_transfer_map,
293 						&st_transfer_map,
294 						"alloc_ht_order"
295 	);
296 
297 	if (pmap == 0) {
298 	    int j;
299 
300 	    for (j = 0; j < inext; j++)
301 		gs_free_object(pmem, pocs[j].corder.transfer, "alloc_ht_order");
302 	    gs_free_object(pmem, pocs, "alloc_ht_order");
303 	    return 0;
304 	}
305 	pmap->proc = gs_mapped_transfer;
306 	pmap->id = gs_next_ids(pmem, 1);
307 	pocs[inext].corder.levels = 0;
308 	pocs[inext].corder.bit_data = 0;
309 	pocs[inext].corder.cache = 0;
310 	pocs[inext].corder.transfer = pmap;
311 	pocs[inext].cname = phtc->cname;
312         pocs[inext].comp_number = phtc->comp_number;
313 	comp2order[i] = inext++;
314     }
315 
316     return pocs;
317 }
318 
319 /*
320  *  Build the halftone order for one component.
321  */
322 private int
build_component(gs_ht_component * phtc,gx_ht_order * porder,gs_state * pgs,gs_memory_t * pmem)323 build_component(
324 		   gs_ht_component * phtc,
325 		   gx_ht_order * porder,
326 		   gs_state * pgs,
327 		   gs_memory_t * pmem
328 )
329 {
330     if (phtc->type == ht_type_spot) {
331 	gs_screen_enum senum;
332 	int code;
333 
334 	code = gx_ht_process_screen_memory(&senum,
335 					   pgs,
336 					   &phtc->params.ht_spot.screen,
337 				      phtc->params.ht_spot.accurate_screens,
338 					   pmem
339 	    );
340 	if (code < 0)
341 	    return code;
342 
343 	/* avoid wiping out the transfer structure pointer */
344 	senum.order.transfer = porder->transfer;
345 	*porder = senum.order;
346 
347     } else {			/* ht_type_threshold */
348 	int code;
349 	gx_transfer_map *transfer = porder->transfer;
350 
351 	porder->params.M = phtc->params.ht_threshold.width;
352 	porder->params.N = 0;
353 	porder->params.R = 1;
354 	porder->params.M1 = phtc->params.ht_threshold.height;
355 	porder->params.N1 = 0;
356 	porder->params.R1 = 1;
357 	code = gx_ht_alloc_threshold_order(porder,
358 					   phtc->params.ht_threshold.width,
359 					   phtc->params.ht_threshold.height,
360 					   256,
361 					   pmem
362 	    );
363 	if (code < 0)
364 	    return code;
365 	gx_ht_construct_threshold_order(
366 				porder,
367 				phtc->params.ht_threshold.thresholds.data
368 	    );
369 	/*
370 	 * gx_ht_construct_threshold_order wipes out transfer map pointer,
371 	 * restore it here.
372 	 */
373 	porder->transfer = transfer;
374     }
375 
376     build_transfer_map(phtc, porder->transfer);
377     return 0;
378 }
379 
380 /*
381  * Free an order array and all elements it points to.
382  */
383 private void
free_order_array(gx_ht_order_component * pocs,int num_comps,gs_memory_t * pmem)384 free_order_array(
385 		    gx_ht_order_component * pocs,
386 		    int num_comps,
387 		    gs_memory_t * pmem
388 )
389 {
390     int i;
391 
392     for (i = 0; i < num_comps; i++)
393 	gx_ht_order_release(&(pocs[i].corder), pmem, true);
394     gs_free_object(pmem, pocs, "gs_ht_install");
395 }
396 
397 
398 /*
399  *  Install a gs_ht halftone as the current halftone in the graphic state.
400  */
401 int
gs_ht_install(gs_state * pgs,gs_ht * pht)402 gs_ht_install(
403 		 gs_state * pgs,
404 		 gs_ht * pht
405 )
406 {
407     int code = 0;
408     gs_memory_t *pmem = pht->rc.memory;
409     gx_device_halftone dev_ht;
410     gx_ht_order_component *pocs;
411     byte comp2order[32];	/* ample component to order map */
412     int num_comps = pht->params.ht_multiple.num_comp;
413     int i;
414 
415     /* perform so sanity checks (must have one default component) */
416     if ((code = check_ht(pht)) != 0)
417 	return code;
418 
419     /* allocate the halftone order structure and transfer maps */
420     if ((pocs = alloc_ht_order(pht, pmem, comp2order)) == 0)
421 	return_error(gs_error_VMerror);
422 
423     /* build all of the order for each component */
424     for (i = 0; i < num_comps; i++) {
425 	int j = comp2order[i];
426 
427 	code = build_component(&(pht->params.ht_multiple.components[i]),
428 			       &(pocs[j].corder),
429 			       pgs,
430 			       pmem
431 	    );
432 
433 	if ((code >= 0) && (j != 0)) {
434 	    gx_ht_cache *pcache;
435 
436 	    pcache = gx_ht_alloc_cache(pmem,
437 				       4,
438 				       pocs[j].corder.raster *
439 				       (pocs[j].corder.num_bits /
440 					pocs[j].corder.width) * 4
441 		);
442 
443 	    if (pcache == 0)
444 		code = gs_note_error(gs_error_VMerror);
445 	    else {
446 		pocs[j].corder.cache = pcache;
447 		gx_ht_init_cache(pmem, pcache, &(pocs[j].corder));
448 	    }
449 	}
450 	if (code < 0)
451 	    break;
452     }
453 
454     if (code < 0) {
455 	free_order_array(pocs, num_comps, pmem);
456 	return code;
457     }
458     /* initialize the device halftone structure */
459     dev_ht.rc.memory = pmem;
460     dev_ht.order = pocs[0].corder;	/* Default */
461     if (num_comps == 1) {
462 	/* we have only a Default; we don't need components. */
463 	gs_free_object(pmem, pocs, "gs_ht_install");
464 	dev_ht.components = 0;
465     } else {
466 	dev_ht.components = pocs;
467 	dev_ht.num_comp = num_comps;
468     }
469 
470     /* at last, actually install the halftone in the graphic state */
471     if ((code = gx_ht_install(pgs, (gs_halftone *) pht, &dev_ht)) < 0)
472         gx_device_halftone_release(&dev_ht, pmem);
473     return code;
474 }
475 
476 /* ---------------- Mask-defined halftones ---------------- */
477 
478 /*
479  * Create a halftone order from an array of explicit masks.  This is
480  * silly, because the rendering machinery actually wants masks, but doing
481  * it right seems to require too many changes in existing code.
482  */
483 private int
create_mask_bits(const byte * mask1,const byte * mask2,int width,int height,gx_ht_bit * bits)484 create_mask_bits(const byte * mask1, const byte * mask2,
485 		 int width, int height, gx_ht_bit * bits)
486 {
487     /*
488      * We do this with the slowest, simplest possible algorithm....
489      */
490     int width_bytes = (width + 7) >> 3;
491     int x, y;
492     int count = 0;
493 
494     for (y = 0; y < height; ++y)
495 	for (x = 0; x < width; ++x) {
496 	    int offset = y * width_bytes + (x >> 3);
497 	    byte bit_mask = 0x80 >> (x & 7);
498 
499 	    if ((mask1[offset] ^ mask2[offset]) & bit_mask) {
500 		if (bits)
501 		    gx_ht_construct_bit(&bits[count], width, y * width + x);
502 		++count;
503 	    }
504 	}
505     return count;
506 }
507 private int
create_mask_order(gx_ht_order * porder,gs_state * pgs,const gs_client_order_halftone * phcop,gs_memory_t * mem)508 create_mask_order(gx_ht_order * porder, gs_state * pgs,
509 		  const gs_client_order_halftone * phcop,
510 		  gs_memory_t * mem)
511 {
512     int width_bytes = (phcop->width + 7) >> 3;
513     const byte *masks = (const byte *)phcop->client_data;
514     int bytes_per_mask = width_bytes * phcop->height;
515     const byte *prev_mask;
516     int num_levels = phcop->num_levels;
517     int num_bits = 0;
518     int i;
519     int code;
520 
521     /* Do a first pass to compute how many bits entries will be needed. */
522     for (prev_mask = masks, num_bits = 0, i = 0;
523 	 i < num_levels - 1;
524 	 ++i, prev_mask += bytes_per_mask
525 	)
526 	num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask,
527 				     phcop->width, phcop->height, NULL);
528     code = gx_ht_alloc_client_order(porder, phcop->width, phcop->height,
529 				    num_levels, num_bits, mem);
530     if (code < 0)
531 	return code;
532     /* Fill in the bits and levels entries. */
533     for (prev_mask = masks, num_bits = 0, i = 0;
534 	 i < num_levels - 1;
535 	 ++i, prev_mask += bytes_per_mask
536 	) {
537 	porder->levels[i] = num_bits;
538 	num_bits += create_mask_bits(prev_mask, prev_mask + bytes_per_mask,
539 				     phcop->width, phcop->height,
540 				     ((gx_ht_bit *)porder->bit_data) +
541 				      num_bits);
542     }
543     porder->levels[num_levels - 1] = num_bits;
544     return 0;
545 }
546 
547 /* Define the client-order halftone procedure structure. */
548 private const gs_client_order_ht_procs_t mask_order_procs =
549 {
550     create_mask_order
551 };
552 
553 /*
554  * Define a halftone by an explicit set of masks.  We translate these
555  * internally into a threshold array, since that's what the halftone
556  * rendering machinery knows how to deal with.
557  */
558 int
gs_ht_set_mask_comp(gs_ht * pht,int component_index,int width,int height,int num_levels,const byte * masks,gs_ht_transfer_proc transfer,const void * client_data)559 gs_ht_set_mask_comp(gs_ht * pht,
560 		    int component_index,
561 		    int width, int height, int num_levels,
562 		    const byte * masks,		/* width x height x num_levels bits */
563 		    gs_ht_transfer_proc transfer,
564 		    const void *client_data)
565 {
566     gs_ht_component *phtc =
567     &(pht->params.ht_multiple.components[component_index]);
568 
569     if (component_index >= pht->params.ht_multiple.num_comp)
570 	return_error(gs_error_rangecheck);
571     if (phtc->type != ht_type_none)
572 	return_error(gs_error_invalidaccess);
573 
574     phtc->type = ht_type_client_order;
575     phtc->params.client_order.width = width;
576     phtc->params.client_order.height = height;
577     phtc->params.client_order.num_levels = num_levels;
578     phtc->params.client_order.procs = &mask_order_procs;
579     phtc->params.client_order.client_data = masks;
580     phtc->params.client_order.transfer_closure.proc =
581 	(transfer == 0 ? null_closure_transfer : transfer);
582     phtc->params.client_order.transfer_closure.data = client_data;
583 
584     return 0;
585 
586 }
587