1 /* Copyright (C) 1997, 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: gsshade.c,v 1.17 2005/04/19 12:22:08 igor Exp $ */
18 /* Constructors for shadings */
19 #include "gx.h"
20 #include "gscspace.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gsptype2.h"
24 #include "gxdevcli.h"
25 #include "gxcpath.h"
26 #include "gxcspace.h"
27 #include "gxdcolor.h" /* for filling background rectangle */
28 #include "gxistate.h"
29 #include "gxpaint.h"
30 #include "gxpath.h"
31 #include "gxshade.h"
32 #include "gxshade4.h"
33 #include "gzpath.h"
34 #include "gzcpath.h"
35
36 /* ================ Initialize shadings ================ */
37
38 /* ---------------- Generic services ---------------- */
39
40 /* GC descriptors */
41 private_st_shading();
42 private_st_shading_mesh();
43
44 private
ENUM_PTRS_WITH(shading_mesh_enum_ptrs,gs_shading_mesh_t * psm)45 ENUM_PTRS_WITH(shading_mesh_enum_ptrs, gs_shading_mesh_t *psm)
46 {
47 index -= 2;
48 if (index < st_data_source_max_ptrs)
49 return ENUM_USING(st_data_source, &psm->params.DataSource,
50 sizeof(psm->params.DataSource), index);
51 return ENUM_USING_PREFIX(st_shading, st_data_source_max_ptrs);
52 }
53 ENUM_PTR2(0, gs_shading_mesh_t, params.Function, params.Decode);
54 ENUM_PTRS_END
55
56 private
RELOC_PTRS_WITH(shading_mesh_reloc_ptrs,gs_shading_mesh_t * psm)57 RELOC_PTRS_WITH(shading_mesh_reloc_ptrs, gs_shading_mesh_t *psm)
58 {
59 RELOC_PREFIX(st_shading);
60 RELOC_USING(st_data_source, &psm->params.DataSource,
61 sizeof(psm->params.DataSource));
62 RELOC_PTR2(gs_shading_mesh_t, params.Function, params.Decode);
63 }
64 RELOC_PTRS_END
65
66 /* Check ColorSpace, BBox, and Function (if present). */
67 /* Free variables: params. */
68 private int
check_CBFD(const gs_shading_params_t * params,const gs_function_t * function,const float * domain,int m)69 check_CBFD(const gs_shading_params_t * params,
70 const gs_function_t * function, const float *domain, int m)
71 {
72 int ncomp = gs_color_space_num_components(params->ColorSpace);
73
74 if (ncomp < 0 ||
75 (params->have_BBox &&
76 (params->BBox.p.x > params->BBox.q.x ||
77 params->BBox.p.y > params->BBox.q.y))
78 )
79 return_error(gs_error_rangecheck);
80 if (function != 0) {
81 if (function->params.m != m || function->params.n != ncomp)
82 return_error(gs_error_rangecheck);
83 /*
84 * The Adobe documentation says that the function's domain must
85 * be a superset of the domain defined in the shading dictionary.
86 * However, Adobe implementations apparently don't necessarily
87 * check this ahead of time; therefore, we do the same.
88 */
89 }
90 return 0;
91 }
92
93 /* Check parameters for a mesh shading. */
94 private int
check_mesh(const gs_shading_mesh_params_t * params)95 check_mesh(const gs_shading_mesh_params_t * params)
96 {
97 const float *domain;
98
99 if (data_source_is_array(params->DataSource))
100 domain = 0;
101 else {
102 domain = params->Decode;
103 switch (params->BitsPerCoordinate) {
104 case 1: case 2: case 4: case 8:
105 case 12: case 16: case 24: case 32:
106 break;
107 default:
108 return_error(gs_error_rangecheck);
109 }
110 switch (params->BitsPerComponent) {
111 case 1: case 2: case 4: case 8:
112 case 12: case 16:
113 break;
114 default:
115 return_error(gs_error_rangecheck);
116 }
117 }
118 return check_CBFD((const gs_shading_params_t *)params,
119 params->Function, domain, 1);
120 }
121
122 /* Check the BitsPerFlag value. Return the value or an error code. */
123 private int
check_BPF(const gs_data_source_t * pds,int bpf)124 check_BPF(const gs_data_source_t *pds, int bpf)
125 {
126 if (data_source_is_array(*pds))
127 return 2;
128 switch (bpf) {
129 case 2: case 4: case 8:
130 return bpf;
131 default:
132 return_error(gs_error_rangecheck);
133 }
134 }
135
136 /* Initialize common shading parameters. */
137 private void
shading_params_init(gs_shading_params_t * params)138 shading_params_init(gs_shading_params_t *params)
139 {
140 params->ColorSpace = 0; /* must be set by client */
141 params->Background = 0;
142 params->have_BBox = false;
143 params->AntiAlias = false;
144 }
145
146 /* Initialize common mesh shading parameters. */
147 private void
mesh_shading_params_init(gs_shading_mesh_params_t * params)148 mesh_shading_params_init(gs_shading_mesh_params_t *params)
149 {
150 shading_params_init((gs_shading_params_t *)params);
151 data_source_init_floats(¶ms->DataSource, NULL, 0);/* client must set */
152 /* Client must set BitsPerCoordinate and BitsPerComponent */
153 /* if DataSource is not an array. */
154 params->Decode = 0;
155 params->Function = 0;
156 }
157
158 /* Allocate and initialize a shading. */
159 /* Free variables: mem, params, ppsh, psh. */
160 #define ALLOC_SHADING(sttype, stype, sprocs, cname)\
161 BEGIN\
162 psh = gs_alloc_struct(mem, void, sttype, cname);\
163 if ( psh == 0 )\
164 return_error(gs_error_VMerror);\
165 psh->head.type = stype;\
166 psh->head.procs = sprocs;\
167 psh->params = *params;\
168 *ppsh = (gs_shading_t *)psh;\
169 END
170
171 /* ---------------- Function-based shading ---------------- */
172
173 private_st_shading_Fb();
174
175 /* Initialize parameters for a Function-based shading. */
176 void
gs_shading_Fb_params_init(gs_shading_Fb_params_t * params)177 gs_shading_Fb_params_init(gs_shading_Fb_params_t * params)
178 {
179 shading_params_init((gs_shading_params_t *)params);
180 params->Domain[0] = params->Domain[2] = 0;
181 params->Domain[1] = params->Domain[3] = 1;
182 gs_make_identity(¶ms->Matrix);
183 params->Function = 0; /* must be set by client */
184 }
185
186 /* Allocate and initialize a Function-based shading. */
187 private const gs_shading_procs_t shading_Fb_procs = {
188 gs_shading_Fb_fill_rectangle
189 };
190 int
gs_shading_Fb_init(gs_shading_t ** ppsh,const gs_shading_Fb_params_t * params,gs_memory_t * mem)191 gs_shading_Fb_init(gs_shading_t ** ppsh,
192 const gs_shading_Fb_params_t * params, gs_memory_t * mem)
193 {
194 gs_shading_Fb_t *psh;
195 gs_matrix imat;
196 int code = check_CBFD((const gs_shading_params_t *)params,
197 params->Function, params->Domain, 2);
198
199 if (code < 0 ||
200 (code = gs_matrix_invert(¶ms->Matrix, &imat)) < 0
201 )
202 return code;
203 ALLOC_SHADING(&st_shading_Fb, shading_type_Function_based,
204 shading_Fb_procs, "gs_shading_Fb_init");
205 return 0;
206 }
207
208 /* ---------------- Axial shading ---------------- */
209
210 private_st_shading_A();
211
212 /* Initialize parameters for an Axial shading. */
213 void
gs_shading_A_params_init(gs_shading_A_params_t * params)214 gs_shading_A_params_init(gs_shading_A_params_t * params)
215 {
216 shading_params_init((gs_shading_params_t *)params);
217 /* Coords must be set by client */
218 params->Domain[0] = 0;
219 params->Domain[1] = 1;
220 params->Function = 0; /* must be set by client */
221 params->Extend[0] = params->Extend[1] = false;
222 }
223
224 /* Allocate and initialize an Axial shading. */
225 private const gs_shading_procs_t shading_A_procs = {
226 gs_shading_A_fill_rectangle
227 };
228 int
gs_shading_A_init(gs_shading_t ** ppsh,const gs_shading_A_params_t * params,gs_memory_t * mem)229 gs_shading_A_init(gs_shading_t ** ppsh,
230 const gs_shading_A_params_t * params, gs_memory_t * mem)
231 {
232 gs_shading_A_t *psh;
233 int code = check_CBFD((const gs_shading_params_t *)params,
234 params->Function, params->Domain, 1);
235
236 if (code < 0)
237 return code;
238 ALLOC_SHADING(&st_shading_A, shading_type_Axial,
239 shading_A_procs, "gs_shading_A_init");
240 return 0;
241 }
242
243 /* ---------------- Radial shading ---------------- */
244
245 private_st_shading_R();
246
247 /* Initialize parameters for a Radial shading. */
248 void
gs_shading_R_params_init(gs_shading_R_params_t * params)249 gs_shading_R_params_init(gs_shading_R_params_t * params)
250 {
251 shading_params_init((gs_shading_params_t *)params);
252 /* Coords must be set by client */
253 params->Domain[0] = 0;
254 params->Domain[1] = 1;
255 params->Function = 0; /* must be set by client */
256 params->Extend[0] = params->Extend[1] = false;
257 }
258
259 /* Allocate and initialize a Radial shading. */
260 private const gs_shading_procs_t shading_R_procs = {
261 gs_shading_R_fill_rectangle
262 };
263 int
gs_shading_R_init(gs_shading_t ** ppsh,const gs_shading_R_params_t * params,gs_memory_t * mem)264 gs_shading_R_init(gs_shading_t ** ppsh,
265 const gs_shading_R_params_t * params, gs_memory_t * mem)
266 {
267 gs_shading_R_t *psh;
268 int code = check_CBFD((const gs_shading_params_t *)params,
269 params->Function, params->Domain, 1);
270
271 if (code < 0)
272 return code;
273 if ((params->Domain != 0 && params->Domain[0] == params->Domain[1]) ||
274 params->Coords[2] < 0 || params->Coords[5] < 0
275 )
276 return_error(gs_error_rangecheck);
277 ALLOC_SHADING(&st_shading_R, shading_type_Radial,
278 shading_R_procs, "gs_shading_R_init");
279 return 0;
280 }
281
282 /* ---------------- Free-form Gouraud triangle mesh shading ---------------- */
283
284 private_st_shading_FfGt();
285
286 /* Initialize parameters for a Free-form Gouraud triangle mesh shading. */
287 void
gs_shading_FfGt_params_init(gs_shading_FfGt_params_t * params)288 gs_shading_FfGt_params_init(gs_shading_FfGt_params_t * params)
289 {
290 mesh_shading_params_init((gs_shading_mesh_params_t *)params);
291 /* Client must set BitsPerFlag if DataSource is not an array. */
292 }
293
294 /* Allocate and initialize a Free-form Gouraud triangle mesh shading. */
295 private const gs_shading_procs_t shading_FfGt_procs = {
296 gs_shading_FfGt_fill_rectangle
297 };
298 int
gs_shading_FfGt_init(gs_shading_t ** ppsh,const gs_shading_FfGt_params_t * params,gs_memory_t * mem)299 gs_shading_FfGt_init(gs_shading_t ** ppsh,
300 const gs_shading_FfGt_params_t * params,
301 gs_memory_t * mem)
302 {
303 gs_shading_FfGt_t *psh;
304 int code = check_mesh((const gs_shading_mesh_params_t *)params);
305 int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag);
306
307 if (code < 0)
308 return code;
309 if (bpf < 0)
310 return bpf;
311 if (params->Decode != 0 && params->Decode[0] == params->Decode[1])
312 return_error(gs_error_rangecheck);
313 ALLOC_SHADING(&st_shading_FfGt, shading_type_Free_form_Gouraud_triangle,
314 shading_FfGt_procs, "gs_shading_FfGt_init");
315 psh->params.BitsPerFlag = bpf;
316 return 0;
317 }
318
319 /* -------------- Lattice-form Gouraud triangle mesh shading -------------- */
320
321 private_st_shading_LfGt();
322
323 /* Initialize parameters for a Lattice-form Gouraud triangle mesh shading. */
324 void
gs_shading_LfGt_params_init(gs_shading_LfGt_params_t * params)325 gs_shading_LfGt_params_init(gs_shading_LfGt_params_t * params)
326 {
327 mesh_shading_params_init((gs_shading_mesh_params_t *)params);
328 /* Client must set VerticesPerRow. */
329 }
330
331 /* Allocate and initialize a Lattice-form Gouraud triangle mesh shading. */
332 private const gs_shading_procs_t shading_LfGt_procs = {
333 gs_shading_LfGt_fill_rectangle
334 };
335 int
gs_shading_LfGt_init(gs_shading_t ** ppsh,const gs_shading_LfGt_params_t * params,gs_memory_t * mem)336 gs_shading_LfGt_init(gs_shading_t ** ppsh,
337 const gs_shading_LfGt_params_t * params, gs_memory_t * mem)
338 {
339 gs_shading_LfGt_t *psh;
340 int code = check_mesh((const gs_shading_mesh_params_t *)params);
341
342 if (code < 0)
343 return code;
344 if (params->VerticesPerRow < 2)
345 return_error(gs_error_rangecheck);
346 ALLOC_SHADING(&st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle,
347 shading_LfGt_procs, "gs_shading_LfGt_init");
348 return 0;
349 }
350
351 /* ---------------- Coons patch mesh shading ---------------- */
352
353 private_st_shading_Cp();
354
355 /* Initialize parameters for a Coons patch mesh shading. */
356 void
gs_shading_Cp_params_init(gs_shading_Cp_params_t * params)357 gs_shading_Cp_params_init(gs_shading_Cp_params_t * params)
358 {
359 mesh_shading_params_init((gs_shading_mesh_params_t *)params);
360 /* Client must set BitsPerFlag if DataSource is not an array. */
361 }
362
363 /* Allocate and initialize a Coons patch mesh shading. */
364 private const gs_shading_procs_t shading_Cp_procs = {
365 gs_shading_Cp_fill_rectangle
366 };
367 int
gs_shading_Cp_init(gs_shading_t ** ppsh,const gs_shading_Cp_params_t * params,gs_memory_t * mem)368 gs_shading_Cp_init(gs_shading_t ** ppsh,
369 const gs_shading_Cp_params_t * params, gs_memory_t * mem)
370 {
371 gs_shading_Cp_t *psh;
372 int code = check_mesh((const gs_shading_mesh_params_t *)params);
373 int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag);
374
375 if (code < 0)
376 return code;
377 if (bpf < 0)
378 return bpf;
379 ALLOC_SHADING(&st_shading_Cp, shading_type_Coons_patch,
380 shading_Cp_procs, "gs_shading_Cp_init");
381 psh->params.BitsPerFlag = bpf;
382 return 0;
383 }
384
385 /* ---------------- Tensor product patch mesh shading ---------------- */
386
387 private_st_shading_Tpp();
388
389 /* Initialize parameters for a Tensor product patch mesh shading. */
390 void
gs_shading_Tpp_params_init(gs_shading_Tpp_params_t * params)391 gs_shading_Tpp_params_init(gs_shading_Tpp_params_t * params)
392 {
393 mesh_shading_params_init((gs_shading_mesh_params_t *)params);
394 /* Client must set BitsPerFlag if DataSource is not an array. */
395 }
396
397 /* Allocate and initialize a Tensor product patch mesh shading. */
398 private const gs_shading_procs_t shading_Tpp_procs = {
399 gs_shading_Tpp_fill_rectangle
400 };
401 int
gs_shading_Tpp_init(gs_shading_t ** ppsh,const gs_shading_Tpp_params_t * params,gs_memory_t * mem)402 gs_shading_Tpp_init(gs_shading_t ** ppsh,
403 const gs_shading_Tpp_params_t * params, gs_memory_t * mem)
404 {
405 gs_shading_Tpp_t *psh;
406 int code = check_mesh((const gs_shading_mesh_params_t *)params);
407 int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag);
408
409 if (code < 0)
410 return code;
411 if (bpf < 0)
412 return bpf;
413 ALLOC_SHADING(&st_shading_Tpp, shading_type_Tensor_product_patch,
414 shading_Tpp_procs, "gs_shading_Tpp_init");
415 psh->params.BitsPerFlag = bpf;
416 return 0;
417 }
418
419 /* ================ Shading rendering ================ */
420
421 /* Add a user-space rectangle to a path. */
422 private int
shading_path_add_box(gx_path * ppath,const gs_rect * pbox,const gs_matrix_fixed * pmat)423 shading_path_add_box(gx_path *ppath, const gs_rect *pbox,
424 const gs_matrix_fixed *pmat)
425 {
426 gs_fixed_point pt;
427 gs_fixed_point pts[3];
428 int code;
429
430 if ((code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->p.y,
431 &pt)) < 0 ||
432 (code = gx_path_add_point(ppath, pt.x, pt.y)) < 0 ||
433 (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->p.y,
434 &pts[0])) < 0 ||
435 (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->q.y,
436 &pts[1])) < 0 ||
437 (code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->q.y,
438 &pts[2])) < 0 ||
439 (code = gx_path_add_lines(ppath, pts, 3)) < 0
440 )
441 DO_NOTHING;
442 return code;
443 }
444
445 /* Fill a path with a shading. */
446 private int
gs_shading_fill_path(const gs_shading_t * psh,gx_path * ppath,const gs_fixed_rect * prect,gx_device * orig_dev,gs_imager_state * pis,bool fill_background)447 gs_shading_fill_path(const gs_shading_t *psh, /*const*/ gx_path *ppath,
448 const gs_fixed_rect *prect, gx_device *orig_dev,
449 gs_imager_state *pis, bool fill_background)
450 {
451 gs_memory_t *mem = pis->memory;
452 const gs_matrix_fixed *pmat = &pis->ctm;
453 gx_device *dev = orig_dev;
454 gs_fixed_rect path_box;
455 gs_rect rect;
456 gx_clip_path *path_clip = 0;
457 bool path_clip_set = false;
458 gx_device_clip path_dev;
459 int code = 0;
460
461 if ((*dev_proc(dev, pattern_manage))(dev,
462 gs_no_id, NULL, pattern_manage__shading_area) == 0) {
463 path_clip = gx_cpath_alloc(mem, "shading_fill_path(path_clip)");
464 if (path_clip == 0) {
465 code = gs_note_error(gs_error_VMerror);
466 goto out;
467 }
468 dev_proc(dev, get_clipping_box)(dev, &path_box);
469 if (prect)
470 rect_intersect(path_box, *prect);
471 if (psh->params.have_BBox) {
472 gs_fixed_rect bbox_fixed;
473
474 if ((is_xxyy(pmat) || is_xyyx(pmat)) &&
475 (code = gx_dc_pattern2_shade_bbox_transform2fixed(&psh->params.BBox, pis,
476 &bbox_fixed)) >= 0
477 ) {
478 /* We can fold BBox into the clipping rectangle. */
479 rect_intersect(path_box, bbox_fixed);
480 } else
481 {
482 gx_path *box_path;
483
484 if (path_box.p.x >= path_box.q.x || path_box.p.y >= path_box.q.y)
485 goto out; /* empty rectangle */
486 box_path = gx_path_alloc(mem, "shading_fill_path(box_path)");
487 if (box_path == 0) {
488 code = gs_note_error(gs_error_VMerror);
489 goto out;
490 }
491 if ((code = gx_cpath_from_rectangle(path_clip, &path_box)) < 0 ||
492 (code = shading_path_add_box(box_path, &psh->params.BBox,
493 pmat)) < 0 ||
494 (code = gx_cpath_intersect(path_clip, box_path,
495 gx_rule_winding_number, pis)) < 0
496 )
497 DO_NOTHING;
498 gx_path_free(box_path, "shading_fill_path(box_path)");
499 if (code < 0)
500 goto out;
501 path_clip_set = true;
502 }
503 }
504 if (!path_clip_set) {
505 if (path_box.p.x >= path_box.q.x || path_box.p.y >= path_box.q.y)
506 goto out; /* empty rectangle */
507 if ((code = gx_cpath_from_rectangle(path_clip, &path_box)) < 0)
508 goto out;
509 }
510 if (ppath &&
511 (code =
512 gx_cpath_intersect(path_clip, ppath, gx_rule_winding_number, pis)) < 0
513 )
514 goto out;
515 gx_make_clip_device(&path_dev, &path_clip->rect_list->list);
516 path_dev.target = dev;
517 path_dev.HWResolution[0] = dev->HWResolution[0];
518 path_dev.HWResolution[1] = dev->HWResolution[1];
519 dev = (gx_device *)&path_dev;
520 dev_proc(dev, open_device)(dev);
521 }
522 #if 0 /* doesn't work for 478-01.ps, which sets a big smoothness :
523 makes an assymmetrix domain, and the patch decomposition
524 becomes highly irregular. */
525 { gs_fixed_rect r;
526
527 dev_proc(dev, get_clipping_box)(dev, &r);
528 rect_intersect(path_box, r);
529 }
530 #else
531 dev_proc(dev, get_clipping_box)(dev, &path_box);
532 #endif
533 if (psh->params.Background && fill_background) {
534 const gs_color_space *pcs = psh->params.ColorSpace;
535 gs_client_color cc;
536 gx_device_color dev_color;
537
538 cc = *psh->params.Background;
539 (*pcs->type->restrict_color)(&cc, pcs);
540 (*pcs->type->remap_color)(&cc, pcs, &dev_color, pis,
541 dev, gs_color_select_texture);
542
543 /****** WRONG IF NON-IDEMPOTENT RasterOp ******/
544 code = gx_shade_background(dev, &path_box, &dev_color, pis->log_op);
545 if (code < 0)
546 goto out;
547 }
548 {
549 gs_rect path_rect;
550
551 path_rect.p.x = fixed2float(path_box.p.x);
552 path_rect.p.y = fixed2float(path_box.p.y);
553 path_rect.q.x = fixed2float(path_box.q.x);
554 path_rect.q.y = fixed2float(path_box.q.y);
555 gs_bbox_transform_inverse(&path_rect, (const gs_matrix *)pmat, &rect);
556 }
557 code = gs_shading_fill_rectangle(psh, &rect, &path_box, dev, pis);
558 out:
559 if (path_clip)
560 gx_cpath_free(path_clip, "shading_fill_path(path_clip)");
561 return code;
562 }
563
564 int
gs_shading_fill_path_adjusted(const gs_shading_t * psh,gx_path * ppath,const gs_fixed_rect * prect,gx_device * orig_dev,gs_imager_state * pis,bool fill_background)565 gs_shading_fill_path_adjusted(const gs_shading_t *psh, /*const*/ gx_path *ppath,
566 const gs_fixed_rect *prect, gx_device *orig_dev,
567 gs_imager_state *pis, bool fill_background)
568 {
569 return gs_shading_fill_path(psh, ppath, prect, orig_dev, pis, fill_background);
570 }
571
572