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