/src/ghostpdl/base/gsshade.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Constructors for shadings */ |
18 | | #include "gx.h" |
19 | | #include "gscspace.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gsptype2.h" |
23 | | #include "gxdevcli.h" |
24 | | #include "gxcpath.h" |
25 | | #include "gxcspace.h" |
26 | | #include "gxdcolor.h" /* for filling background rectangle */ |
27 | | #include "gxgstate.h" |
28 | | #include "gxpaint.h" |
29 | | #include "gxpath.h" |
30 | | #include "gxshade.h" |
31 | | #include "gxshade4.h" |
32 | | #include "gzpath.h" |
33 | | #include "gzcpath.h" |
34 | | |
35 | | /* ================ Initialize shadings ================ */ |
36 | | |
37 | | /* ---------------- Generic services ---------------- */ |
38 | | |
39 | | /* GC descriptors */ |
40 | | private_st_shading(); |
41 | | |
42 | | static |
43 | 0 | ENUM_PTRS_WITH(shading_mesh_enum_ptrs, gs_shading_mesh_t *psm) |
44 | 0 | { |
45 | 0 | index -= 2; |
46 | 0 | if (index < st_data_source_max_ptrs) |
47 | 0 | return ENUM_USING(st_data_source, &psm->params.DataSource, |
48 | 0 | sizeof(psm->params.DataSource), index); |
49 | 0 | return ENUM_USING_PREFIX(st_shading, st_data_source_max_ptrs); |
50 | 0 | } |
51 | 0 | ENUM_PTR2(0, gs_shading_mesh_t, params.Function, params.Decode); |
52 | 0 | ENUM_PTRS_END |
53 | | |
54 | | static |
55 | 0 | RELOC_PTRS_WITH(shading_mesh_reloc_ptrs, gs_shading_mesh_t *psm) |
56 | 0 | { |
57 | 0 | RELOC_PREFIX(st_shading); |
58 | 0 | RELOC_USING(st_data_source, &psm->params.DataSource, |
59 | 0 | sizeof(psm->params.DataSource)); |
60 | 0 | RELOC_PTR2(gs_shading_mesh_t, params.Function, params.Decode); |
61 | 0 | } |
62 | 0 | RELOC_PTRS_END |
63 | | |
64 | | /* Check ColorSpace, BBox, and Function (if present). */ |
65 | | /* Free variables: params. */ |
66 | | static int |
67 | | check_CBFD(const gs_shading_params_t * params, |
68 | | const gs_function_t * function, const float *domain, int m) |
69 | 74.8k | { |
70 | 74.8k | int ncomp = gs_color_space_num_components(params->ColorSpace); |
71 | | |
72 | 74.8k | if (ncomp < 0 || |
73 | 74.8k | (params->have_BBox && |
74 | 74.8k | (params->BBox.p.x > params->BBox.q.x || |
75 | 101 | params->BBox.p.y > params->BBox.q.y)) |
76 | 74.8k | ) |
77 | 0 | return_error(gs_error_rangecheck); |
78 | 74.8k | if (function != 0) { |
79 | 72.3k | if (function->params.m != m || function->params.n != ncomp) |
80 | 5 | return_error(gs_error_rangecheck); |
81 | | /* |
82 | | * The Adobe documentation says that the function's domain must |
83 | | * be a superset of the domain defined in the shading dictionary. |
84 | | * However, Adobe implementations apparently don't necessarily |
85 | | * check this ahead of time; therefore, we do the same. |
86 | | */ |
87 | 72.3k | } |
88 | 74.8k | return 0; |
89 | 74.8k | } |
90 | | |
91 | | /* Check parameters for a mesh shading. */ |
92 | | static int |
93 | | check_mesh(const gs_shading_mesh_params_t * params) |
94 | 2.98k | { |
95 | 2.98k | const float *domain; |
96 | | |
97 | 2.98k | if (data_source_is_array(params->DataSource)) |
98 | 0 | domain = 0; |
99 | 2.98k | else { |
100 | 2.98k | domain = params->Decode; |
101 | 2.98k | switch (params->BitsPerCoordinate) { |
102 | 0 | case 1: case 2: case 4: case 8: |
103 | 2.98k | case 12: case 16: case 24: case 32: |
104 | 2.98k | break; |
105 | 0 | default: |
106 | 0 | return_error(gs_error_rangecheck); |
107 | 2.98k | } |
108 | 2.98k | switch (params->BitsPerComponent) { |
109 | 2.04k | case 1: case 2: case 4: case 8: |
110 | 2.98k | case 12: case 16: |
111 | 2.98k | break; |
112 | 0 | default: |
113 | 0 | return_error(gs_error_rangecheck); |
114 | 2.98k | } |
115 | 2.98k | } |
116 | 2.98k | return check_CBFD((const gs_shading_params_t *)params, |
117 | 2.98k | params->Function, domain, 1); |
118 | 2.98k | } |
119 | | |
120 | | /* Check the BitsPerFlag value. Return the value or an error code. */ |
121 | | static int |
122 | | check_BPF(const gs_data_source_t *pds, int bpf) |
123 | 2.58k | { |
124 | 2.58k | if (data_source_is_array(*pds)) |
125 | 0 | return 2; |
126 | 2.58k | switch (bpf) { |
127 | 2.58k | case 2: case 4: case 8: |
128 | 2.58k | return bpf; |
129 | 0 | default: |
130 | 0 | return_error(gs_error_rangecheck); |
131 | 2.58k | } |
132 | 2.58k | } |
133 | | |
134 | | /* Initialize common shading parameters. */ |
135 | | static void |
136 | | shading_params_init(gs_shading_params_t *params) |
137 | 0 | { |
138 | 0 | params->ColorSpace = 0; /* must be set by client */ |
139 | 0 | params->cie_joint_caches = 0; |
140 | 0 | params->Background = 0; |
141 | 0 | params->have_BBox = false; |
142 | 0 | params->AntiAlias = false; |
143 | 0 | } |
144 | | |
145 | | /* Initialize common mesh shading parameters. */ |
146 | | static void |
147 | | mesh_shading_params_init(gs_shading_mesh_params_t *params) |
148 | 0 | { |
149 | 0 | shading_params_init((gs_shading_params_t *)params); |
150 | 0 | data_source_init_floats(¶ms->DataSource, NULL, 0);/* client must set */ |
151 | | /* Client must set BitsPerCoordinate and BitsPerComponent */ |
152 | | /* if DataSource is not an array. */ |
153 | 0 | params->Decode = 0; |
154 | 0 | params->Function = 0; |
155 | 0 | } |
156 | | |
157 | | /* Allocate and initialize a shading. */ |
158 | | #define ALLOC_SHADING(ppsh, psh, mem, sttype, stype, sprocs, cname, params)\ |
159 | 149k | BEGIN\ |
160 | 149k | psh = gs_alloc_struct(mem, void, sttype, cname);\ |
161 | 149k | if ( psh == 0 )\ |
162 | 149k | return_error(gs_error_VMerror);\ |
163 | 149k | psh->head.type = stype;\ |
164 | 74.8k | psh->head.procs = sprocs;\ |
165 | 74.8k | psh->params = *params;\ |
166 | 74.8k | *ppsh = (gs_shading_t *)psh;\ |
167 | 74.8k | END |
168 | | |
169 | | /* ---------------- Function-based shading ---------------- */ |
170 | | |
171 | | private_st_shading_Fb(); |
172 | | |
173 | | /* Initialize parameters for a Function-based shading. */ |
174 | | void |
175 | | gs_shading_Fb_params_init(gs_shading_Fb_params_t * params) |
176 | 0 | { |
177 | 0 | shading_params_init((gs_shading_params_t *)params); |
178 | 0 | params->Domain[0] = params->Domain[2] = 0; |
179 | 0 | params->Domain[1] = params->Domain[3] = 1; |
180 | 0 | gs_make_identity(¶ms->Matrix); |
181 | 0 | params->Function = 0; /* must be set by client */ |
182 | 0 | } |
183 | | |
184 | | /* Allocate and initialize a Function-based shading. */ |
185 | | static const gs_shading_procs_t shading_Fb_procs = { |
186 | | gs_shading_Fb_fill_rectangle |
187 | | }; |
188 | | int |
189 | | gs_shading_Fb_init(gs_shading_t ** ppsh, |
190 | | const gs_shading_Fb_params_t * params, gs_memory_t * mem) |
191 | 0 | { |
192 | 0 | gs_shading_Fb_t *psh; |
193 | 0 | gs_matrix imat; |
194 | 0 | int code = check_CBFD((const gs_shading_params_t *)params, |
195 | 0 | params->Function, params->Domain, 2); |
196 | |
|
197 | 0 | if (code < 0 || |
198 | 0 | (code = gs_matrix_invert(¶ms->Matrix, &imat)) < 0 |
199 | 0 | ) |
200 | 0 | return code; |
201 | 0 | ALLOC_SHADING(ppsh, psh, mem, &st_shading_Fb, shading_type_Function_based, |
202 | 0 | shading_Fb_procs, "gs_shading_Fb_init", params); |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | | /* ---------------- Axial shading ---------------- */ |
207 | | |
208 | | private_st_shading_A(); |
209 | | |
210 | | /* Initialize parameters for an Axial shading. */ |
211 | | void |
212 | | gs_shading_A_params_init(gs_shading_A_params_t * params) |
213 | 0 | { |
214 | 0 | shading_params_init((gs_shading_params_t *)params); |
215 | | /* Coords must be set by client */ |
216 | 0 | params->Domain[0] = 0; |
217 | 0 | params->Domain[1] = 1; |
218 | 0 | params->Function = 0; /* must be set by client */ |
219 | 0 | params->Extend[0] = params->Extend[1] = false; |
220 | 0 | } |
221 | | |
222 | | /* Allocate and initialize an Axial shading. */ |
223 | | static const gs_shading_procs_t shading_A_procs = { |
224 | | gs_shading_A_fill_rectangle |
225 | | }; |
226 | | int |
227 | | gs_shading_A_init(gs_shading_t ** ppsh, |
228 | | const gs_shading_A_params_t * params, gs_memory_t * mem) |
229 | 71.7k | { |
230 | 71.7k | gs_shading_A_t *psh; |
231 | 71.7k | int code = check_CBFD((const gs_shading_params_t *)params, |
232 | 71.7k | params->Function, params->Domain, 1); |
233 | | |
234 | 71.7k | if (code < 0) |
235 | 5 | return code; |
236 | 71.7k | ALLOC_SHADING(ppsh, psh, mem, &st_shading_A, shading_type_Axial, |
237 | 71.6k | shading_A_procs, "gs_shading_A_init", params); |
238 | 71.6k | return 0; |
239 | 71.6k | } |
240 | | |
241 | | /* ---------------- Radial shading ---------------- */ |
242 | | |
243 | | private_st_shading_R(); |
244 | | |
245 | | /* Initialize parameters for a Radial shading. */ |
246 | | void |
247 | | gs_shading_R_params_init(gs_shading_R_params_t * params) |
248 | 0 | { |
249 | 0 | shading_params_init((gs_shading_params_t *)params); |
250 | | /* Coords must be set by client */ |
251 | 0 | params->Domain[0] = 0; |
252 | 0 | params->Domain[1] = 1; |
253 | 0 | params->Function = 0; /* must be set by client */ |
254 | 0 | params->Extend[0] = params->Extend[1] = false; |
255 | 0 | } |
256 | | |
257 | | /* Allocate and initialize a Radial shading. */ |
258 | | static const gs_shading_procs_t shading_R_procs = { |
259 | | gs_shading_R_fill_rectangle |
260 | | }; |
261 | | int |
262 | | gs_shading_R_init(gs_shading_t ** ppsh, |
263 | | const gs_shading_R_params_t * params, gs_memory_t * mem) |
264 | 180 | { |
265 | 180 | gs_shading_R_t *psh; |
266 | 180 | int code; |
267 | | |
268 | 180 | if (params == NULL || params->Domain[0] == params->Domain[1] || |
269 | 180 | params->Coords[2] < 0 || params->Coords[5] < 0) |
270 | 0 | return_error(gs_error_rangecheck); |
271 | 180 | code = check_CBFD((const gs_shading_params_t *)params, |
272 | 180 | params->Function, params->Domain, 1); |
273 | | |
274 | 180 | if (code < 0) |
275 | 0 | return code; |
276 | 180 | ALLOC_SHADING(ppsh, psh, mem, &st_shading_R, shading_type_Radial, |
277 | 180 | shading_R_procs, "gs_shading_R_init", params); |
278 | 180 | return 0; |
279 | 180 | } |
280 | | |
281 | | /* ---------------- Free-form Gouraud triangle mesh shading ---------------- */ |
282 | | |
283 | | private_st_shading_FfGt(); |
284 | | |
285 | | /* Initialize parameters for a Free-form Gouraud triangle mesh shading. */ |
286 | | void |
287 | | gs_shading_FfGt_params_init(gs_shading_FfGt_params_t * params) |
288 | 0 | { |
289 | 0 | mesh_shading_params_init((gs_shading_mesh_params_t *)params); |
290 | | /* Client must set BitsPerFlag if DataSource is not an array. */ |
291 | 0 | } |
292 | | |
293 | | /* Allocate and initialize a Free-form Gouraud triangle mesh shading. */ |
294 | | static const gs_shading_procs_t shading_FfGt_procs = { |
295 | | gs_shading_FfGt_fill_rectangle |
296 | | }; |
297 | | int |
298 | | gs_shading_FfGt_init(gs_shading_t ** ppsh, |
299 | | const gs_shading_FfGt_params_t * params, |
300 | | gs_memory_t * mem) |
301 | 360 | { |
302 | 360 | gs_shading_FfGt_t *psh; |
303 | 360 | int code = check_mesh((const gs_shading_mesh_params_t *)params); |
304 | 360 | int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag); |
305 | | |
306 | 360 | if (code < 0) |
307 | 0 | return code; |
308 | 360 | if (bpf < 0) |
309 | 0 | return bpf; |
310 | 360 | ALLOC_SHADING(ppsh, psh, mem, &st_shading_FfGt, shading_type_Free_form_Gouraud_triangle, |
311 | 360 | shading_FfGt_procs, "gs_shading_FfGt_init", params); |
312 | 360 | psh->params.BitsPerFlag = bpf; |
313 | 360 | return 0; |
314 | 360 | } |
315 | | |
316 | | /* -------------- Lattice-form Gouraud triangle mesh shading -------------- */ |
317 | | |
318 | | private_st_shading_LfGt(); |
319 | | |
320 | | /* Initialize parameters for a Lattice-form Gouraud triangle mesh shading. */ |
321 | | void |
322 | | gs_shading_LfGt_params_init(gs_shading_LfGt_params_t * params) |
323 | 0 | { |
324 | 0 | mesh_shading_params_init((gs_shading_mesh_params_t *)params); |
325 | | /* Client must set VerticesPerRow. */ |
326 | 0 | } |
327 | | |
328 | | /* Allocate and initialize a Lattice-form Gouraud triangle mesh shading. */ |
329 | | static const gs_shading_procs_t shading_LfGt_procs = { |
330 | | gs_shading_LfGt_fill_rectangle |
331 | | }; |
332 | | int |
333 | | gs_shading_LfGt_init(gs_shading_t ** ppsh, |
334 | | const gs_shading_LfGt_params_t * params, gs_memory_t * mem) |
335 | 405 | { |
336 | 405 | gs_shading_LfGt_t *psh; |
337 | 405 | int code = check_mesh((const gs_shading_mesh_params_t *)params); |
338 | | |
339 | 405 | if (code < 0) |
340 | 0 | return code; |
341 | 405 | if (params->VerticesPerRow < 2) |
342 | 0 | return_error(gs_error_rangecheck); |
343 | 405 | ALLOC_SHADING(ppsh, psh, mem, &st_shading_LfGt, shading_type_Lattice_form_Gouraud_triangle, |
344 | 405 | shading_LfGt_procs, "gs_shading_LfGt_init", params); |
345 | 405 | return 0; |
346 | 405 | } |
347 | | |
348 | | /* ---------------- Coons patch mesh shading ---------------- */ |
349 | | |
350 | | private_st_shading_Cp(); |
351 | | |
352 | | /* Initialize parameters for a Coons patch mesh shading. */ |
353 | | void |
354 | | gs_shading_Cp_params_init(gs_shading_Cp_params_t * params) |
355 | 0 | { |
356 | 0 | mesh_shading_params_init((gs_shading_mesh_params_t *)params); |
357 | | /* Client must set BitsPerFlag if DataSource is not an array. */ |
358 | 0 | } |
359 | | |
360 | | /* Allocate and initialize a Coons patch mesh shading. */ |
361 | | static const gs_shading_procs_t shading_Cp_procs = { |
362 | | gs_shading_Cp_fill_rectangle |
363 | | }; |
364 | | int |
365 | | gs_shading_Cp_init(gs_shading_t ** ppsh, |
366 | | const gs_shading_Cp_params_t * params, gs_memory_t * mem) |
367 | 171 | { |
368 | 171 | gs_shading_Cp_t *psh; |
369 | 171 | int code = check_mesh((const gs_shading_mesh_params_t *)params); |
370 | 171 | int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag); |
371 | | |
372 | 171 | if (code < 0) |
373 | 0 | return code; |
374 | 171 | if (bpf < 0) |
375 | 0 | return bpf; |
376 | 171 | ALLOC_SHADING(ppsh, psh, mem, &st_shading_Cp, shading_type_Coons_patch, |
377 | 171 | shading_Cp_procs, "gs_shading_Cp_init", params); |
378 | 171 | psh->params.BitsPerFlag = bpf; |
379 | 171 | return 0; |
380 | 171 | } |
381 | | |
382 | | /* ---------------- Tensor product patch mesh shading ---------------- */ |
383 | | |
384 | | private_st_shading_Tpp(); |
385 | | |
386 | | /* Initialize parameters for a Tensor product patch mesh shading. */ |
387 | | void |
388 | | gs_shading_Tpp_params_init(gs_shading_Tpp_params_t * params) |
389 | 0 | { |
390 | 0 | mesh_shading_params_init((gs_shading_mesh_params_t *)params); |
391 | | /* Client must set BitsPerFlag if DataSource is not an array. */ |
392 | 0 | } |
393 | | |
394 | | /* Allocate and initialize a Tensor product patch mesh shading. */ |
395 | | static const gs_shading_procs_t shading_Tpp_procs = { |
396 | | gs_shading_Tpp_fill_rectangle |
397 | | }; |
398 | | int |
399 | | gs_shading_Tpp_init(gs_shading_t ** ppsh, |
400 | | const gs_shading_Tpp_params_t * params, gs_memory_t * mem) |
401 | 2.05k | { |
402 | 2.05k | gs_shading_Tpp_t *psh; |
403 | 2.05k | int code = check_mesh((const gs_shading_mesh_params_t *)params); |
404 | 2.05k | int bpf = check_BPF(¶ms->DataSource, params->BitsPerFlag); |
405 | | |
406 | 2.05k | if (code < 0) |
407 | 0 | return code; |
408 | 2.05k | if (bpf < 0) |
409 | 0 | return bpf; |
410 | 2.05k | ALLOC_SHADING(ppsh, psh, mem, &st_shading_Tpp, shading_type_Tensor_product_patch, |
411 | 2.05k | shading_Tpp_procs, "gs_shading_Tpp_init", params); |
412 | 2.05k | psh->params.BitsPerFlag = bpf; |
413 | 2.05k | return 0; |
414 | 2.05k | } |
415 | | |
416 | | /* ================ Shading rendering ================ */ |
417 | | |
418 | | /* Add a user-space rectangle to a path. */ |
419 | | int |
420 | | gs_shading_path_add_box(gx_path *ppath, const gs_rect *pbox, |
421 | | const gs_matrix_fixed *pmat) |
422 | 93 | { |
423 | 93 | gs_fixed_point pt; |
424 | 93 | gs_fixed_point pts[3]; |
425 | 93 | int code; |
426 | | |
427 | 93 | if ((code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->p.y, |
428 | 93 | &pt)) < 0 || |
429 | 93 | (code = gx_path_add_point(ppath, pt.x, pt.y)) < 0 || |
430 | 93 | (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->p.y, |
431 | 93 | &pts[0])) < 0 || |
432 | 93 | (code = gs_point_transform2fixed(pmat, pbox->q.x, pbox->q.y, |
433 | 93 | &pts[1])) < 0 || |
434 | 93 | (code = gs_point_transform2fixed(pmat, pbox->p.x, pbox->q.y, |
435 | 93 | &pts[2])) < 0 || |
436 | 93 | (code = gx_path_add_lines(ppath, pts, 3)) < 0 |
437 | 93 | ) |
438 | 93 | DO_NOTHING; |
439 | 93 | return code; |
440 | 93 | } |
441 | | |
442 | | /* Fill a path with a shading. */ |
443 | | int |
444 | | gs_shading_do_fill_rectangle(const gs_shading_t *psh, |
445 | | const gs_fixed_rect *prect, gx_device *dev, |
446 | | gs_gstate *pgs, bool fill_background) |
447 | 267k | { /* If you need to fill a path, clip the output device before calling this function. */ |
448 | 267k | const gs_matrix_fixed *pmat = &pgs->ctm; |
449 | 267k | gs_fixed_rect path_box; |
450 | 267k | gs_rect path_rect; |
451 | 267k | gs_rect rect; |
452 | 267k | int code = 0; |
453 | | |
454 | 267k | dev_proc(dev, get_clipping_box)(dev, &path_box); |
455 | 267k | if (prect) |
456 | 267k | rect_intersect(path_box, *prect); |
457 | 267k | if (psh->params.Background && fill_background) { |
458 | 201k | const gs_color_space *pcs = psh->params.ColorSpace; |
459 | 201k | gs_client_color cc; |
460 | 201k | gx_device_color dev_color; |
461 | | |
462 | 201k | cc = *psh->params.Background; |
463 | 201k | (*pcs->type->restrict_color)(&cc, pcs); |
464 | 201k | code = (*pcs->type->remap_color)(&cc, pcs, &dev_color, pgs, |
465 | 201k | dev, gs_color_select_texture); |
466 | | |
467 | | /****** WRONG IF NON-IDEMPOTENT RasterOp ******/ |
468 | 201k | if (code >= 0) |
469 | 201k | code = gx_shade_background(dev, &path_box, &dev_color, pgs->log_op); |
470 | 201k | } |
471 | 267k | if (code >= 0) { |
472 | 267k | path_rect.p.x = fixed2float(path_box.p.x); |
473 | 267k | path_rect.p.y = fixed2float(path_box.p.y); |
474 | 267k | path_rect.q.x = fixed2float(path_box.q.x); |
475 | 267k | path_rect.q.y = fixed2float(path_box.q.y); |
476 | 267k | code = gs_bbox_transform_inverse(&path_rect, (const gs_matrix *)pmat, &rect); |
477 | 267k | if (code >= 0) |
478 | 267k | code = gs_shading_fill_rectangle(psh, &rect, &path_box, dev, pgs); |
479 | 267k | } |
480 | 267k | return code; |
481 | 267k | } |