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, ¶ms);
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, ¶ms);
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