xref: /plan9/sys/src/cmd/gs/src/zdpnext.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 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: zdpnext.c,v 1.8 2005/03/14 18:08:37 dan Exp $ */
18 /* NeXT Display PostScript extensions */
19 #include "math_.h"
20 #include "ghost.h"
21 #include "oper.h"
22 #include "gscoord.h"
23 #include "gscspace.h"		/* for iimage.h */
24 #include "gsdpnext.h"
25 #include "gsmatrix.h"
26 #include "gsiparam.h"		/* for iimage.h */
27 #include "gsiparm2.h"
28 #include "gspath2.h"
29 #include "gxcvalue.h"
30 #include "gxdevice.h"
31 #include "gxsample.h"
32 #include "ialloc.h"
33 #include "igstate.h"
34 #include "iimage.h"
35 #include "iimage2.h"
36 #include "store.h"
37 
38 /* ------ alpha channel ------ */
39 
40 /* - currentalpha <alpha> */
41 private int
zcurrentalpha(i_ctx_t * i_ctx_p)42 zcurrentalpha(i_ctx_t *i_ctx_p)
43 {
44     os_ptr op = osp;
45 
46     push(1);
47     make_real(op, gs_currentalpha(igs));
48     return 0;
49 }
50 
51 /* <alpha> setalpha - */
52 private int
zsetalpha(i_ctx_t * i_ctx_p)53 zsetalpha(i_ctx_t *i_ctx_p)
54 {
55     os_ptr op = osp;
56     double alpha;
57     int code;
58 
59     if (real_param(op, &alpha) < 0)
60 	return_op_typecheck(op);
61     if ((code = gs_setalpha(igs, alpha)) < 0)
62 	return code;
63     pop(1);
64     return 0;
65 }
66 
67 /* ------ Imaging/compositing ------ */
68 
69 /*
70  * Miscellaneous notes:
71  *
72  * composite / dissolve respect destination clipping (both clip & viewclip),
73  *   but ignore source clipping.
74  * composite / dissolve must handle overlapping source/destination correctly.
75  * compositing converts the source to the destination's color model
76  *   (including halftoning if needed).
77  */
78 
79 /*
80  * Define the operand and bookeeping structure for a compositing operation.
81  */
82 typedef struct alpha_composite_state_s {
83     /* Compositing parameters */
84     gs_composite_alpha_params_t params;
85     /* Temporary structures */
86     gs_composite_t *pcte;
87     gx_device *cdev;
88     gx_device *orig_dev;
89 } alpha_composite_state_t;
90 
91 /* Forward references */
92 private int begin_composite(i_ctx_t *, alpha_composite_state_t *);
93 private void end_composite(i_ctx_t *, alpha_composite_state_t *);
94 private int xywh_param(os_ptr, double[4]);
95 
96 /* <dict> .alphaimage - */
97 /* This is the dictionary version of the alphaimage operator, which is */
98 /* now a pseudo-operator (see gs_dpnxt.ps). */
99 private int
zalphaimage(i_ctx_t * i_ctx_p)100 zalphaimage(i_ctx_t *i_ctx_p)
101 {
102     return image1_setup(i_ctx_p, true);
103 }
104 
105 /* <destx> <desty> <width> <height> <op> compositerect - */
106 private int
zcompositerect(i_ctx_t * i_ctx_p)107 zcompositerect(i_ctx_t *i_ctx_p)
108 {
109     os_ptr op = osp;
110     double dest_rect[4];
111     alpha_composite_state_t cstate;
112     int code = xywh_param(op - 1, dest_rect);
113 
114     if (code < 0)
115 	return code;
116     check_int_leu(*op, compositerect_last);
117     cstate.params.op = (gs_composite_op_t) op->value.intval;
118     code = begin_composite(i_ctx_p, &cstate);
119     if (code < 0)
120 	return code;
121     {
122 	gs_rect rect;
123 
124 	rect.q.x = (rect.p.x = dest_rect[0]) + dest_rect[2];
125 	rect.q.y = (rect.p.y = dest_rect[1]) + dest_rect[3];
126 	code = gs_rectfill(igs, &rect, 1);
127     }
128     end_composite(i_ctx_p, &cstate);
129     if (code >= 0)
130 	pop(5);
131     return code;
132 }
133 
134 /* Common code for composite and dissolve. */
135 private int
composite_image(i_ctx_t * i_ctx_p,const gs_composite_alpha_params_t * params)136 composite_image(i_ctx_t *i_ctx_p, const gs_composite_alpha_params_t * params)
137 {
138     os_ptr op = osp;
139     alpha_composite_state_t cstate;
140     gs_image2_t image;
141     double src_rect[4];
142     double dest_pt[2];
143     gs_matrix save_ctm;
144     int code = xywh_param(op - 4, src_rect);
145 
146     cstate.params = *params;
147     gs_image2_t_init(&image);
148     if (code < 0 ||
149 	(code = num_params(op - 1, 2, dest_pt)) < 0
150 	)
151 	return code;
152     if (r_has_type(op - 3, t_null))
153 	image.DataSource = igs;
154     else {
155 	check_stype(op[-3], st_igstate_obj);
156 	check_read(op[-3]);
157 	image.DataSource = igstate_ptr(op - 3);
158     }
159     image.XOrigin = src_rect[0];
160     image.YOrigin = src_rect[1];
161     image.Width = src_rect[2];
162     image.Height = src_rect[3];
163     image.PixelCopy = true;
164     /* Compute appropriate transformations. */
165     gs_currentmatrix(igs, &save_ctm);
166     gs_translate(igs, dest_pt[0], dest_pt[1]);
167     gs_make_identity(&image.ImageMatrix);
168     if (image.DataSource == igs) {
169 	image.XOrigin -= dest_pt[0];
170 	image.YOrigin -= dest_pt[1];
171     }
172     code = begin_composite(i_ctx_p, &cstate);
173     if (code >= 0) {
174 	code = process_non_source_image(i_ctx_p,
175 					(const gs_image_common_t *)&image,
176 					"composite_image");
177 	end_composite(i_ctx_p, &cstate);
178 	if (code >= 0)
179 	    pop(8);
180     }
181     gs_setmatrix(igs, &save_ctm);
182     return code;
183 }
184 
185 /* <srcx> <srcy> <width> <height> <srcgstate|null> <destx> <desty> <op> */
186 /*   composite - */
187 private int
zcomposite(i_ctx_t * i_ctx_p)188 zcomposite(i_ctx_t *i_ctx_p)
189 {
190     os_ptr op = osp;
191     gs_composite_alpha_params_t params;
192 
193     check_int_leu(*op, composite_last);
194     params.op = (gs_composite_op_t) op->value.intval;
195     return composite_image(i_ctx_p, &params);
196 }
197 
198 /* <srcx> <srcy> <width> <height> <srcgstate|null> <destx> <desty> <delta> */
199 /*   dissolve - */
200 private int
zdissolve(i_ctx_t * i_ctx_p)201 zdissolve(i_ctx_t *i_ctx_p)
202 {
203     os_ptr op = osp;
204     gs_composite_alpha_params_t params;
205     double delta;
206     int code = real_param(op, &delta);
207 
208     if (code < 0)
209 	return code;
210     if (delta < 0 || delta > 1)
211 	return_error(e_rangecheck);
212     params.op = composite_Dissolve;
213     params.delta = delta;
214     return composite_image(i_ctx_p, &params);
215 }
216 
217 /* ------ Image reading ------ */
218 
219 private int device_is_true_color(gx_device * dev);
220 
221 /* <x> <y> <width> <height> <matrix> .sizeimagebox */
222 /*   <dev_x> <dev_y> <dev_width> <dev_height> <matrix> */
223 private void box_confine(int *pp, int *pq, int wh);
224 private int
zsizeimagebox(i_ctx_t * i_ctx_p)225 zsizeimagebox(i_ctx_t *i_ctx_p)
226 {
227     os_ptr op = osp;
228     const gx_device *dev = gs_currentdevice(igs);
229     gs_rect srect, drect;
230     gs_matrix mat;
231     gs_int_rect rect;
232     int w, h;
233     int code;
234 
235     check_type(op[-4], t_integer);
236     check_type(op[-3], t_integer);
237     check_type(op[-2], t_integer);
238     check_type(op[-1], t_integer);
239     srect.p.x = op[-4].value.intval;
240     srect.p.y = op[-3].value.intval;
241     srect.q.x = srect.p.x + op[-2].value.intval;
242     srect.q.y = srect.p.y + op[-1].value.intval;
243     gs_currentmatrix(igs, &mat);
244     gs_bbox_transform(&srect, &mat, &drect);
245     /*
246      * We want the dimensions of the image as a source, not a
247      * destination, so we need to expand it rather than pixround.
248      */
249     rect.p.x = (int)floor(drect.p.x);
250     rect.p.y = (int)floor(drect.p.y);
251     rect.q.x = (int)ceil(drect.q.x);
252     rect.q.y = (int)ceil(drect.q.y);
253     /*
254      * Clip the rectangle to the device boundaries, since that's what
255      * the NeXT implementation does.
256      */
257     box_confine(&rect.p.x, &rect.q.x, dev->width);
258     box_confine(&rect.p.y, &rect.q.y, dev->height);
259     w = rect.q.x - rect.p.x;
260     h = rect.q.y - rect.p.y;
261     /*
262      * The NeXT documentation doesn't specify very clearly what is
263      * supposed to be in the matrix: the following produces results
264      * that match testing on an actual NeXT system.
265      */
266     mat.tx -= rect.p.x;
267     mat.ty -= rect.p.y;
268     code = write_matrix(op, &mat);
269     if (code < 0)
270 	return code;
271     make_int(op - 4, rect.p.x);
272     make_int(op - 3, rect.p.y);
273     make_int(op - 2, w);
274     make_int(op - 1, h);
275     return 0;
276 }
277 private void
box_confine(int * pp,int * pq,int wh)278 box_confine(int *pp, int *pq, int wh)
279 {
280     if ( *pq <= 0 )
281 	*pp = *pq = 0;
282     else if ( *pp >= wh )
283 	*pp = *pq = wh;
284     else {
285 	if ( *pp < 0 )
286 	    *pp = 0;
287 	if ( *pq > wh )
288 	    *pq = wh;
289     }
290 }
291 
292 /* - .sizeimageparams <bits/sample> <multiproc> <ncolors> */
293 private int
zsizeimageparams(i_ctx_t * i_ctx_p)294 zsizeimageparams(i_ctx_t *i_ctx_p)
295 {
296     os_ptr op = osp;
297     gx_device *dev = gs_currentdevice(igs);
298     int ncomp = dev->color_info.num_components;
299     int bps;
300 
301     push(3);
302     if (device_is_true_color(dev))
303 	bps = dev->color_info.depth / ncomp;
304     else {
305 	/*
306 	 * Set bps to the smallest allowable number of bits that is
307 	 * sufficient to represent the number of different colors.
308 	 */
309 	gx_color_value max_value =
310 	    (dev->color_info.num_components == 1 ?
311 	     dev->color_info.max_gray :
312 	     max(dev->color_info.max_gray, dev->color_info.max_color));
313 	static const gx_color_value sizes[] = {
314 	    1, 2, 4, 8, 12, sizeof(gx_max_color_value) * 8
315 	};
316 	int i;
317 
318 	for (i = 0;; ++i)
319 	    if (max_value <= ((ulong) 1 << sizes[i]) - 1)
320 		break;
321 	bps = sizes[i];
322     }
323     make_int(op - 2, bps);
324     make_false(op - 1);
325     make_int(op, ncomp);
326     return 0;
327 }
328 
329 /* ------ Initialization procedure ------ */
330 
331 const op_def zdpnext_op_defs[] =
332 {
333     {"0currentalpha", zcurrentalpha},
334     {"1setalpha", zsetalpha},
335     {"1.alphaimage", zalphaimage},
336     {"8composite", zcomposite},
337     {"5compositerect", zcompositerect},
338     {"8dissolve", zdissolve},
339     {"5.sizeimagebox", zsizeimagebox},
340     {"0.sizeimageparams", zsizeimageparams},
341     op_def_end(0)
342 };
343 
344 /* ------ Internal routines ------ */
345 
346 /* Collect a rect operand. */
347 private int
xywh_param(os_ptr op,double rect[4])348 xywh_param(os_ptr op, double rect[4])
349 {
350     int code = num_params(op, 4, rect);
351 
352     if (code < 0)
353 	return code;
354     if (rect[2] < 0)
355 	rect[0] += rect[2], rect[2] = -rect[2];
356     if (rect[3] < 0)
357 	rect[1] += rect[3], rect[3] = -rect[3];
358     return code;
359 }
360 
361 /* Begin a compositing operation. */
362 private int
begin_composite(i_ctx_t * i_ctx_p,alpha_composite_state_t * pcp)363 begin_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp)
364 {
365     gx_device *dev = gs_currentdevice(igs);
366     int code =
367 	gs_create_composite_alpha(&pcp->pcte, &pcp->params, imemory);
368 
369     if (code < 0)
370 	return code;
371     pcp->orig_dev = pcp->cdev = dev;	/* for end_composite */
372     code = (*dev_proc(dev, create_compositor))
373 	(dev, &pcp->cdev, pcp->pcte, (gs_imager_state *)igs, imemory);
374     if (code < 0) {
375 	end_composite(i_ctx_p, pcp);
376 	return code;
377     }
378     gs_setdevice_no_init(igs, pcp->cdev);
379     return 0;
380 }
381 
382 /* End a compositing operation. */
383 private void
end_composite(i_ctx_t * i_ctx_p,alpha_composite_state_t * pcp)384 end_composite(i_ctx_t *i_ctx_p, alpha_composite_state_t * pcp)
385 {
386     /* Close and free the compositor and the compositing object. */
387     if (pcp->cdev != pcp->orig_dev) {
388 	gs_closedevice(pcp->cdev);	/* also frees the device */
389 	gs_setdevice_no_init(igs, pcp->orig_dev);
390     }
391     ifree_object(pcp->pcte, "end_composite(gs_composite_t)");
392 }
393 
394 /*
395  * Determine whether a device has decomposed pixels with the components
396  * in the standard PostScript order, and a 1-for-1 color map
397  * (possibly inverted).  Return 0 if not true color, 1 if true color,
398  * -1 if inverted true color.
399  */
400 private int
device_is_true_color(gx_device * dev)401 device_is_true_color(gx_device * dev)
402 {
403     int ncomp = dev->color_info.num_components;
404     int depth = dev->color_info.depth;
405     int i, max_v;
406 
407 #define CV(i) (gx_color_value)((ulong)gx_max_color_value * i / max_v)
408 #define CV0 ((gx_color_value)0)
409 
410     /****** DOESN'T HANDLE INVERSION YET ******/
411     switch (ncomp) {
412 	case 1:		/* gray-scale */
413 	    max_v = dev->color_info.max_gray;
414 	    if (max_v != (1 << depth) - 1)
415 		return 0;
416 	    for (i = 0; i <= max_v; ++i) {
417 		gx_color_value v[3];
418                 v[0] = v[1] = v[2] = CV(i);
419 		if ((*dev_proc(dev, map_rgb_color)) (dev, v) != i)
420 		    return 0;
421 	    }
422 	    return true;
423 	case 3:		/* RGB */
424 	    max_v = dev->color_info.max_color;
425 	    if (depth % 3 != 0 || max_v != (1 << (depth / 3)) - 1)
426 		return false;
427 	    {
428 		const int gs = depth / 3, rs = gs * 2;
429 
430 		for (i = 0; i <= max_v; ++i) {
431 		    gx_color_value red[3];
432                     gx_color_value green[3];
433                     gx_color_value blue[3];
434                     red[0] = CV(i); red[1] = CV0, red[2] = CV0;
435                     green[0] = CV0; green[1] = CV(i); green[2] = CV0;
436                     blue[0] = CV0; blue[1] = CV0; blue[2] = CV(i);
437 		    if ((*dev_proc(dev, map_rgb_color)) (dev, red) !=
438 			i << rs ||
439 			(*dev_proc(dev, map_rgb_color)) (dev, green) !=
440 			i << gs ||
441 			(*dev_proc(dev, map_rgb_color)) (dev, blue) !=
442 			i	/*<< bs */
443 			)
444 			return 0;
445 		}
446 	    }
447 	    return true;
448 	case 4:		/* CMYK */
449 	    max_v = dev->color_info.max_color;
450 	    if ((depth & 3) != 0 || max_v != (1 << (depth / 4)) - 1)
451 		return false;
452 	    {
453 		const int ys = depth / 4, ms = ys * 2, cs = ys * 3;
454 
455 		for (i = 0; i <= max_v; ++i) {
456 
457 		    gx_color_value cyan[4];
458                     gx_color_value magenta[4];
459                     gx_color_value yellow[4];
460                     gx_color_value black[4];
461                     cyan[0] = CV(i); cyan[1] = cyan[2] = cyan[3] = CV0;
462                     magenta[1] = CV(i); magenta[0] = magenta[2] = magenta[3] = CV0;
463                     yellow[2] = CV(i); yellow[0] = yellow[1] = yellow[3] = CV0;
464                     black[3] = CV(i); black[0] = black[1] = black[2] = CV0;
465 		    if ((*dev_proc(dev, map_cmyk_color)) (dev, cyan) !=
466 			i << cs ||
467 			(*dev_proc(dev, map_cmyk_color)) (dev, magenta) !=
468 			i << ms ||
469 			(*dev_proc(dev, map_cmyk_color)) (dev, yellow) !=
470 			i << ys ||
471 			(*dev_proc(dev, map_cmyk_color)) (dev, black) !=
472 			i	/*<< ks */
473 			)
474 			return 0;
475 		}
476 	    }
477 	    return 1;
478 	default:
479 	    return 0;		/* DeviceN */
480     }
481 #undef CV
482 #undef CV0
483 }
484