1 /* Copyright (C) 2001-2006 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, modified
8 or distributed except as expressly authorized under the terms of that
9 license. Refer to licensing information at http://www.artifex.com/
10 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
12 */
13
14 /* $Id: gxshade1.c 10230 2009-10-27 16:13:23Z robin $ */
15 /* Rendering for non-mesh shadings */
16 #include "math_.h"
17 #include "memory_.h"
18 #include "gx.h"
19 #include "gserrors.h"
20 #include "gsmatrix.h" /* for gscoord.h */
21 #include "gscoord.h"
22 #include "gspath.h"
23 #include "gsptype2.h"
24 #include "gxcspace.h"
25 #include "gxdcolor.h"
26 #include "gxfarith.h"
27 #include "gxfixed.h"
28 #include "gxistate.h"
29 #include "gxpath.h"
30 #include "gxshade.h"
31 #include "gxdevcli.h"
32 #include "gxshade4.h"
33 #include "vdtrace.h"
34
35 #define VD_TRACE_AXIAL_PATCH 1
36 #define VD_TRACE_RADIAL_PATCH 1
37 #define VD_TRACE_FUNCTIONAL_PATCH 1
38
39
40 /* ---------------- Function-based shading ---------------- */
41
42 typedef struct Fb_frame_s { /* A rudiment of old code. */
43 gs_rect region;
44 gs_client_color cc[4]; /* colors at 4 corners */
45 int state;
46 } Fb_frame_t;
47
48 typedef struct Fb_fill_state_s {
49 shading_fill_state_common;
50 const gs_shading_Fb_t *psh;
51 gs_matrix_fixed ptm; /* parameter space -> device space */
52 Fb_frame_t frame;
53 } Fb_fill_state_t;
54 /****** NEED GC DESCRIPTOR ******/
55
56 static inline void
make_other_poles(patch_curve_t curve[4])57 make_other_poles(patch_curve_t curve[4])
58 {
59 int i, j;
60
61 for (i = 0; i < 4; i++) {
62 j = (i + 1) % 4;
63 curve[i].control[0].x = (curve[i].vertex.p.x * 2 + curve[j].vertex.p.x) / 3;
64 curve[i].control[0].y = (curve[i].vertex.p.y * 2 + curve[j].vertex.p.y) / 3;
65 curve[i].control[1].x = (curve[i].vertex.p.x + curve[j].vertex.p.x * 2) / 3;
66 curve[i].control[1].y = (curve[i].vertex.p.y + curve[j].vertex.p.y * 2) / 3;
67 curve[i].straight = true;
68 }
69 }
70
71 static int
Fb_fill_region(Fb_fill_state_t * pfs,const gs_fixed_rect * rect)72 Fb_fill_region(Fb_fill_state_t * pfs, const gs_fixed_rect *rect)
73 {
74 patch_fill_state_t pfs1;
75 patch_curve_t curve[4];
76 Fb_frame_t * fp = &pfs->frame;
77 int code;
78
79 if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s')) {
80 vd_get_dc('s');
81 vd_set_shift(0, 0);
82 vd_set_scale(0.01);
83 vd_set_origin(0, 0);
84 }
85 memcpy(&pfs1, (shading_fill_state_t *)pfs, sizeof(shading_fill_state_t));
86 pfs1.Function = pfs->psh->params.Function;
87 code = init_patch_fill_state(&pfs1);
88 if (code < 0)
89 return code;
90 pfs1.maybe_self_intersecting = false;
91 pfs1.n_color_args = 2;
92 pfs1.rect = *rect;
93 gs_point_transform2fixed(&pfs->ptm, fp->region.p.x, fp->region.p.y, &curve[0].vertex.p);
94 gs_point_transform2fixed(&pfs->ptm, fp->region.q.x, fp->region.p.y, &curve[1].vertex.p);
95 gs_point_transform2fixed(&pfs->ptm, fp->region.q.x, fp->region.q.y, &curve[2].vertex.p);
96 gs_point_transform2fixed(&pfs->ptm, fp->region.p.x, fp->region.q.y, &curve[3].vertex.p);
97 make_other_poles(curve);
98 curve[0].vertex.cc[0] = fp->region.p.x; curve[0].vertex.cc[1] = fp->region.p.y;
99 curve[1].vertex.cc[0] = fp->region.q.x; curve[1].vertex.cc[1] = fp->region.p.y;
100 curve[2].vertex.cc[0] = fp->region.q.x; curve[2].vertex.cc[1] = fp->region.q.y;
101 curve[3].vertex.cc[0] = fp->region.p.x; curve[3].vertex.cc[1] = fp->region.q.y;
102 code = patch_fill(&pfs1, curve, NULL, NULL);
103 if (term_patch_fill_state(&pfs1))
104 return_error(gs_error_unregistered); /* Must not happen. */
105 if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s'))
106 vd_release_dc;
107 return code;
108 }
109
110 int
gs_shading_Fb_fill_rectangle(const gs_shading_t * psh0,const gs_rect * rect,const gs_fixed_rect * rect_clip,gx_device * dev,gs_imager_state * pis)111 gs_shading_Fb_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
112 const gs_fixed_rect * rect_clip,
113 gx_device * dev, gs_imager_state * pis)
114 {
115 const gs_shading_Fb_t * const psh = (const gs_shading_Fb_t *)psh0;
116 gs_matrix save_ctm;
117 int xi, yi;
118 float x[2], y[2];
119 Fb_fill_state_t state;
120
121 shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis);
122 state.psh = psh;
123 /****** HACK FOR FIXED-POINT MATRIX MULTIPLY ******/
124 gs_currentmatrix((gs_state *) pis, &save_ctm);
125 gs_concat((gs_state *) pis, &psh->params.Matrix);
126 state.ptm = pis->ctm;
127 gs_setmatrix((gs_state *) pis, &save_ctm);
128 /* Compute the parameter X and Y ranges. */
129 {
130 gs_rect pbox;
131
132 gs_bbox_transform_inverse(rect, &psh->params.Matrix, &pbox);
133 x[0] = max(pbox.p.x, psh->params.Domain[0]);
134 x[1] = min(pbox.q.x, psh->params.Domain[1]);
135 y[0] = max(pbox.p.y, psh->params.Domain[2]);
136 y[1] = min(pbox.q.y, psh->params.Domain[3]);
137 }
138 if (x[0] > x[1] || y[0] > y[1]) {
139 /* The region is outside the shading area. */
140 return 0;
141 }
142 for (xi = 0; xi < 2; ++xi)
143 for (yi = 0; yi < 2; ++yi) {
144 float v[2];
145
146 v[0] = x[xi], v[1] = y[yi];
147 gs_function_evaluate(psh->params.Function, v,
148 state.frame.cc[yi * 2 + xi].paint.values);
149 }
150 state.frame.region.p.x = x[0];
151 state.frame.region.p.y = y[0];
152 state.frame.region.q.x = x[1];
153 state.frame.region.q.y = y[1];
154 return Fb_fill_region(&state, rect_clip);
155 }
156
157 /* ---------------- Axial shading ---------------- */
158
159 typedef struct A_fill_state_s {
160 const gs_shading_A_t *psh;
161 gs_point delta;
162 double length;
163 double t0, t1;
164 double v0, v1, u0, u1;
165 } A_fill_state_t;
166 /****** NEED GC DESCRIPTOR ******/
167
168 /* Note t0 and t1 vary over [0..1], not the Domain. */
169
170 static int
A_fill_region(A_fill_state_t * pfs,patch_fill_state_t * pfs1)171 A_fill_region(A_fill_state_t * pfs, patch_fill_state_t *pfs1)
172 {
173 const gs_shading_A_t * const psh = pfs->psh;
174 double x0 = psh->params.Coords[0] + pfs->delta.x * pfs->v0;
175 double y0 = psh->params.Coords[1] + pfs->delta.y * pfs->v0;
176 double x1 = psh->params.Coords[0] + pfs->delta.x * pfs->v1;
177 double y1 = psh->params.Coords[1] + pfs->delta.y * pfs->v1;
178 double h0 = pfs->u0, h1 = pfs->u1;
179 patch_curve_t curve[4];
180
181 gs_point_transform2fixed(&pfs1->pis->ctm, x0 + pfs->delta.y * h0, y0 - pfs->delta.x * h0, &curve[0].vertex.p);
182 gs_point_transform2fixed(&pfs1->pis->ctm, x1 + pfs->delta.y * h0, y1 - pfs->delta.x * h0, &curve[1].vertex.p);
183 gs_point_transform2fixed(&pfs1->pis->ctm, x1 + pfs->delta.y * h1, y1 - pfs->delta.x * h1, &curve[2].vertex.p);
184 gs_point_transform2fixed(&pfs1->pis->ctm, x0 + pfs->delta.y * h1, y0 - pfs->delta.x * h1, &curve[3].vertex.p);
185 curve[0].vertex.cc[0] = pfs->t0; /* The element cc[1] is set to a dummy value against */
186 curve[1].vertex.cc[0] = pfs->t1; /* interrupts while an idle priocessing in gxshade.6.c . */
187 curve[2].vertex.cc[0] = pfs->t1;
188 curve[3].vertex.cc[0] = pfs->t0;
189 curve[0].vertex.cc[1] = 0; /* The element cc[1] is set to a dummy value against */
190 curve[1].vertex.cc[1] = 0; /* interrupts while an idle priocessing in gxshade.6.c . */
191 curve[2].vertex.cc[1] = 0;
192 curve[3].vertex.cc[1] = 0;
193 make_other_poles(curve);
194 return patch_fill(pfs1, curve, NULL, NULL);
195 }
196
197 static inline int
gs_shading_A_fill_rectangle_aux(const gs_shading_t * psh0,const gs_rect * rect,const gs_fixed_rect * clip_rect,gx_device * dev,gs_imager_state * pis)198 gs_shading_A_fill_rectangle_aux(const gs_shading_t * psh0, const gs_rect * rect,
199 const gs_fixed_rect *clip_rect,
200 gx_device * dev, gs_imager_state * pis)
201 {
202 const gs_shading_A_t *const psh = (const gs_shading_A_t *)psh0;
203 gs_function_t * const pfn = psh->params.Function;
204 gs_matrix cmat;
205 gs_rect t_rect;
206 A_fill_state_t state;
207 patch_fill_state_t pfs1;
208 float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1];
209 float dd = d1 - d0;
210 double t0, t1;
211 gs_point dist;
212 int code;
213
214 state.psh = psh;
215 shade_init_fill_state((shading_fill_state_t *)&pfs1, psh0, dev, pis);
216 pfs1.Function = pfn;
217 pfs1.rect = *clip_rect;
218 code = init_patch_fill_state(&pfs1);
219 if (code < 0)
220 return code;
221 pfs1.maybe_self_intersecting = false;
222 pfs1.function_arg_shift = 1;
223 /*
224 * Compute the parameter range. We construct a matrix in which
225 * (0,0) corresponds to t = 0 and (0,1) corresponds to t = 1,
226 * and use it to inverse-map the rectangle to be filled.
227 */
228 cmat.tx = psh->params.Coords[0];
229 cmat.ty = psh->params.Coords[1];
230 state.delta.x = psh->params.Coords[2] - psh->params.Coords[0];
231 state.delta.y = psh->params.Coords[3] - psh->params.Coords[1];
232 cmat.yx = state.delta.x;
233 cmat.yy = state.delta.y;
234 cmat.xx = cmat.yy;
235 cmat.xy = -cmat.yx;
236 gs_bbox_transform_inverse(rect, &cmat, &t_rect);
237 t0 = min(max(t_rect.p.y, 0), 1);
238 t1 = max(min(t_rect.q.y, 1), 0);
239 state.v0 = t0;
240 state.v1 = t1;
241 state.u0 = t_rect.p.x;
242 state.u1 = t_rect.q.x;
243 state.t0 = t0 * dd + d0;
244 state.t1 = t1 * dd + d0;
245 gs_distance_transform(state.delta.x, state.delta.y, &ctm_only(pis),
246 &dist);
247 state.length = hypot(dist.x, dist.y); /* device space line length */
248 code = A_fill_region(&state, &pfs1);
249 if (psh->params.Extend[0] && t0 > t_rect.p.y) {
250 if (code < 0)
251 return code;
252 /* Use the general algorithm, because we need the trapping. */
253 state.v0 = t_rect.p.y;
254 state.v1 = t0;
255 state.t0 = state.t1 = t0 * dd + d0;
256 code = A_fill_region(&state, &pfs1);
257 }
258 if (psh->params.Extend[1] && t1 < t_rect.q.y) {
259 if (code < 0)
260 return code;
261 /* Use the general algorithm, because we need the trapping. */
262 state.v0 = t1;
263 state.v1 = t_rect.q.y;
264 state.t0 = state.t1 = t1 * dd + d0;
265 code = A_fill_region(&state, &pfs1);
266 }
267 if (term_patch_fill_state(&pfs1))
268 return_error(gs_error_unregistered); /* Must not happen. */
269 return code;
270 }
271
272 int
gs_shading_A_fill_rectangle(const gs_shading_t * psh0,const gs_rect * rect,const gs_fixed_rect * rect_clip,gx_device * dev,gs_imager_state * pis)273 gs_shading_A_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
274 const gs_fixed_rect * rect_clip,
275 gx_device * dev, gs_imager_state * pis)
276 {
277 int code;
278
279 if (VD_TRACE_AXIAL_PATCH && vd_allowed('s')) {
280 vd_get_dc('s');
281 vd_set_shift(0, 0);
282 vd_set_scale(0.01);
283 vd_set_origin(0, 0);
284 }
285 code = gs_shading_A_fill_rectangle_aux(psh0, rect, rect_clip, dev, pis);
286 if (VD_TRACE_AXIAL_PATCH && vd_allowed('s'))
287 vd_release_dc;
288 return code;
289 }
290
291 /* ---------------- Radial shading ---------------- */
292
293 static int
R_tensor_annulus(patch_fill_state_t * pfs,const gs_rect * rect,double x0,double y0,double r0,double t0,double x1,double y1,double r1,double t1)294 R_tensor_annulus(patch_fill_state_t *pfs, const gs_rect *rect,
295 double x0, double y0, double r0, double t0,
296 double x1, double y1, double r1, double t1)
297 {
298 double dx = x1 - x0, dy = y1 - y0;
299 double d = hypot(dx, dy);
300 gs_point p0, p1, pc0, pc1;
301 int k, j, code;
302 bool inside = 0;
303
304 pc0.x = x0, pc0.y = y0;
305 pc1.x = x1, pc1.y = y1;
306 if (r0 + d <= r1 || r1 + d <= r0) {
307 /* One circle is inside another one.
308 Use any subdivision,
309 but don't depend on dx, dy, which may be too small. */
310 p0.x = 0, p0.y = -1;
311 /* Align stripes along radii for faster triangulation : */
312 inside = 1;
313 } else {
314 /* Must generate canonic quadrangle arcs,
315 because we approximate them with curves. */
316 if(any_abs(dx) >= any_abs(dy)) {
317 if (dx > 0)
318 p0.x = 0, p0.y = -1;
319 else
320 p0.x = 0, p0.y = 1;
321 } else {
322 if (dy > 0)
323 p0.x = 1, p0.y = 0;
324 else
325 p0.x = -1, p0.y = 0;
326 }
327 }
328 /* fixme: wish: cut invisible parts off.
329 Note : when r0 != r1 the invisible part is not a half circle. */
330 for (k = 0; k < 4; k++, p0 = p1) {
331 gs_point p[12];
332 patch_curve_t curve[4];
333
334 p1.x = -p0.y; p1.y = p0.x;
335 if ((k & 1) == k >> 1) {
336 make_quadrant_arc(p + 0, &pc0, &p1, &p0, r0);
337 make_quadrant_arc(p + 6, &pc1, &p0, &p1, r1);
338 } else {
339 make_quadrant_arc(p + 0, &pc0, &p0, &p1, r0);
340 make_quadrant_arc(p + 6, &pc1, &p1, &p0, r1);
341 }
342 p[4].x = (p[3].x * 2 + p[6].x) / 3;
343 p[4].y = (p[3].y * 2 + p[6].y) / 3;
344 p[5].x = (p[3].x + p[6].x * 2) / 3;
345 p[5].y = (p[3].y + p[6].y * 2) / 3;
346 p[10].x = (p[9].x * 2 + p[0].x) / 3;
347 p[10].y = (p[9].y * 2 + p[0].y) / 3;
348 p[11].x = (p[9].x + p[0].x * 2) / 3;
349 p[11].y = (p[9].y + p[0].y * 2) / 3;
350 for (j = 0; j < 4; j++) {
351 int jj = (j + inside) % 4;
352
353 code = gs_point_transform2fixed(&pfs->pis->ctm,
354 p[j * 3 + 0].x, p[j * 3 + 0].y, &curve[jj].vertex.p);
355 if (code < 0)
356 return code;
357 code = gs_point_transform2fixed(&pfs->pis->ctm,
358 p[j * 3 + 1].x, p[j * 3 + 1].y, &curve[jj].control[0]);
359 if (code < 0)
360 return code;
361 code = gs_point_transform2fixed(&pfs->pis->ctm,
362 p[j * 3 + 2].x, p[j * 3 + 2].y, &curve[jj].control[1]);
363 if (code < 0)
364 return code;
365 curve[j].straight = (((j + inside) & 1) != 0);
366 }
367 curve[(0 + inside) % 4].vertex.cc[0] = t0;
368 curve[(1 + inside) % 4].vertex.cc[0] = t0;
369 curve[(2 + inside) % 4].vertex.cc[0] = t1;
370 curve[(3 + inside) % 4].vertex.cc[0] = t1;
371 curve[0].vertex.cc[1] = curve[1].vertex.cc[1] = 0; /* Initialize against FPE. */
372 curve[2].vertex.cc[1] = curve[3].vertex.cc[1] = 0; /* Initialize against FPE. */
373 code = patch_fill(pfs, curve, NULL, NULL);
374 if (code < 0)
375 return code;
376 }
377 return 0;
378 }
379
380
381 static int
R_outer_circle(patch_fill_state_t * pfs,const gs_rect * rect,double x0,double y0,double r0,double x1,double y1,double r1,double * x2,double * y2,double * r2)382 R_outer_circle(patch_fill_state_t *pfs, const gs_rect *rect,
383 double x0, double y0, double r0,
384 double x1, double y1, double r1,
385 double *x2, double *y2, double *r2)
386 {
387 double dx = x1 - x0, dy = y1 - y0;
388 double sp, sq, s;
389
390 /* Compute a cone circle, which contacts the rect externally. */
391 /* Don't bother with all 4 sides of the rect,
392 just do with the X or Y span only,
393 so it's not an exact contact, sorry. */
394 if (any_abs(dx) > any_abs(dy)) {
395 /* Solving :
396 x0 + (x1 - x0) * sq + r0 + (r1 - r0) * sq == bbox_px
397 (x1 - x0) * sp + (r1 - r0) * sp == bbox_px - x0 - r0
398 sp = (bbox_px - x0 - r0) / (x1 - x0 + r1 - r0)
399
400 x0 + (x1 - x0) * sq - r0 - (r1 - r0) * sq == bbox_qx
401 (x1 - x0) * sq - (r1 - r0) * sq == bbox_x - x0 + r0
402 sq = (bbox_x - x0 + r0) / (x1 - x0 - r1 + r0)
403 */
404 if (x1 - x0 + r1 - r0 == 0) /* We checked for obtuse cone. */
405 return_error(gs_error_unregistered); /* Must not happen. */
406 if (x1 - x0 - r1 + r0 == 0) /* We checked for obtuse cone. */
407 return_error(gs_error_unregistered); /* Must not happen. */
408 sp = (rect->p.x - x0 - r0) / (x1 - x0 + r1 - r0);
409 sq = (rect->q.x - x0 + r0) / (x1 - x0 - r1 + r0);
410 } else {
411 /* Same by Y. */
412 if (y1 - y0 + r1 - r0 == 0) /* We checked for obtuse cone. */
413 return_error(gs_error_unregistered); /* Must not happen. */
414 if (y1 - y0 - r1 + r0 == 0) /* We checked for obtuse cone. */
415 return_error(gs_error_unregistered); /* Must not happen. */
416 sp = (rect->p.y - y0 - r0) / (y1 - y0 + r1 - r0);
417 sq = (rect->q.y - y0 + r0) / (y1 - y0 - r1 + r0);
418 }
419 if (sp >= 1 && sq >= 1)
420 s = max(sp, sq);
421 else if(sp >= 1)
422 s = sp;
423 else if (sq >= 1)
424 s = sq;
425 else {
426 /* The circle 1 is outside the rect, use it. */
427 s = 1;
428 }
429 if (r0 + (r1 - r0) * s < 0) {
430 /* Passed the cone apex, use the apex. */
431 s = r0 / (r0 - r1);
432 *r2 = 0;
433 } else
434 *r2 = r0 + (r1 - r0) * s;
435 *x2 = x0 + (x1 - x0) * s;
436 *y2 = y0 + (y1 - y0) * s;
437 return 0;
438 }
439
440 static double
R_rect_radius(const gs_rect * rect,double x0,double y0)441 R_rect_radius(const gs_rect *rect, double x0, double y0)
442 {
443 double d, dd;
444
445 dd = hypot(rect->p.x - x0, rect->p.y - y0);
446 d = hypot(rect->p.x - x0, rect->q.y - y0);
447 dd = max(dd, d);
448 d = hypot(rect->q.x - x0, rect->q.y - y0);
449 dd = max(dd, d);
450 d = hypot(rect->q.x - x0, rect->p.y - y0);
451 dd = max(dd, d);
452 return dd;
453 }
454
455 static int
R_fill_triangle_new(patch_fill_state_t * pfs,const gs_rect * rect,double x0,double y0,double x1,double y1,double x2,double y2,double t)456 R_fill_triangle_new(patch_fill_state_t *pfs, const gs_rect *rect,
457 double x0, double y0, double x1, double y1, double x2, double y2, double t)
458 {
459 shading_vertex_t p0, p1, p2;
460 patch_color_t *c;
461 int code;
462 reserve_colors(pfs, &c, 1); /* Can't fail */
463
464 p0.c = c;
465 p1.c = c;
466 p2.c = c;
467 code = gs_point_transform2fixed(&pfs->pis->ctm, x0, y0, &p0.p);
468 if (code >= 0)
469 code = gs_point_transform2fixed(&pfs->pis->ctm, x1, y1, &p1.p);
470 if (code >= 0)
471 code = gs_point_transform2fixed(&pfs->pis->ctm, x2, y2, &p2.p);
472 if (code >= 0) {
473 c->t[0] = c->t[1] = t;
474 patch_resolve_color(c, pfs);
475 code = mesh_triangle(pfs, &p0, &p1, &p2);
476 }
477 release_colors(pfs, pfs->color_stack, 1);
478 return code;
479 }
480
481 static int
R_obtuse_cone(patch_fill_state_t * pfs,const gs_rect * rect,double x0,double y0,double r0,double x1,double y1,double r1,double t0,double r_rect,bool inwards)482 R_obtuse_cone(patch_fill_state_t *pfs, const gs_rect *rect,
483 double x0, double y0, double r0,
484 double x1, double y1, double r1, double t0, double r_rect,
485 bool inwards)
486 {
487 double dx = x1 - x0, dy = y1 - y0, dr = any_abs(r1 - r0);
488 double d = hypot(dx, dy);
489 /* Assuming dr > d / 3 && d > dr + 1e-7 * (d + dr), see the caller. */
490 double r = r_rect * 1.4143; /* A few bigger than sqrt(2). */
491 double ax, ay, as; /* Cone apex. */
492 double g0; /* The distance from apex to the tangent point of the 0th circle. */
493 int code;
494
495 as = r0 / (r0 - r1);
496 ax = x0 + (x1 - x0) * as;
497 ay = y0 + (y1 - y0) * as;
498 g0 = sqrt(dx * dx + dy * dy - dr * dr) * as;
499 if (g0 < 1e-7 * r0) {
500 /* Nearly degenerate, replace with half-plane. */
501 /* Restrict the half plane with triangle, which covers the rect. */
502 gs_point p0, p1, p2; /* Right tangent limit, apex limit, left tangent linit,
503 (right, left == when looking from the apex). */
504
505 p0.x = ax - dy * r / d;
506 p0.y = ay + dx * r / d;
507 p1.x = ax - dx * r / d;
508 p1.y = ay - dy * r / d;
509 p2.x = ax + dy * r / d;
510 p2.y = ay - dx * r / d;
511 /* Split into 2 triangles at the apex,
512 so that the apex is preciselly covered.
513 Especially important when it is not exactly degenerate. */
514 code = R_fill_triangle_new(pfs, rect, ax, ay, p0.x, p0.y, p1.x, p1.y, t0);
515 if (code < 0)
516 return code;
517 return R_fill_triangle_new(pfs, rect, ax, ay, p1.x, p1.y, p2.x, p2.y, t0);
518 } else {
519 /* Compute the "limit" circle so that its
520 tangent points are outside the rect. */
521 /* Note: this branch is executed when the condition above is false :
522 g0 >= 1e-7 * r0 .
523 We believe that computing this branch with doubles
524 provides enough precision after converting coordinates into 'fixed',
525 and that the limit circle radius is not dramatically big.
526 */
527 double es, er; /* The limit circle parameter, radius. */
528 double ex, ey; /* The limit circle centrum. */
529
530 es = as - as * r / g0; /* Always negative. */
531 er = r * r0 / g0 ;
532 ex = x0 + dx * es;
533 ey = y0 + dy * es;
534 /* Fill the annulus: */
535 code = R_tensor_annulus(pfs, rect, x0, y0, r0, t0, ex, ey, er, t0);
536 if (code < 0)
537 return code;
538 /* Fill entire ending circle to ensure entire rect is covered, but
539 * only if we are filling "inwards" (as otherwise we will overwrite
540 * all the hard work we have done to this point) */
541 if (inwards)
542 code = R_tensor_annulus(pfs, rect, ex, ey, er, t0, ex, ey, 0, t0);
543 return code;
544 }
545 }
546
547 static int
R_tensor_cone_apex(patch_fill_state_t * pfs,const gs_rect * rect,double x0,double y0,double r0,double x1,double y1,double r1,double t)548 R_tensor_cone_apex(patch_fill_state_t *pfs, const gs_rect *rect,
549 double x0, double y0, double r0,
550 double x1, double y1, double r1, double t)
551 {
552 double as = r0 / (r0 - r1);
553 double ax = x0 + (x1 - x0) * as;
554 double ay = y0 + (y1 - y0) * as;
555
556 return R_tensor_annulus(pfs, rect, x1, y1, r1, t, ax, ay, 0, t);
557 }
558
559
560 static int
R_extensions(patch_fill_state_t * pfs,const gs_shading_R_t * psh,const gs_rect * rect,double t0,double t1,bool Extend0,bool Extend1)561 R_extensions(patch_fill_state_t *pfs, const gs_shading_R_t *psh, const gs_rect *rect,
562 double t0, double t1, bool Extend0, bool Extend1)
563 {
564 float x0 = psh->params.Coords[0], y0 = psh->params.Coords[1];
565 floatp r0 = psh->params.Coords[2];
566 float x1 = psh->params.Coords[3], y1 = psh->params.Coords[4];
567 floatp r1 = psh->params.Coords[5];
568 double dx = x1 - x0, dy = y1 - y0, dr = any_abs(r1 - r0);
569 double d = hypot(dx, dy), r;
570 int code;
571
572 if (dr >= d - 1e-7 * (d + dr)) {
573 /* Nested circles, or degenerate. */
574 if (r0 > r1) {
575 if (Extend0) {
576 r = R_rect_radius(rect, x0, y0);
577 if (r > r0) {
578 code = R_tensor_annulus(pfs, rect, x0, y0, r, t0, x0, y0, r0, t0);
579 if (code < 0)
580 return code;
581 }
582 }
583 if (Extend1 && r1 > 0)
584 return R_tensor_annulus(pfs, rect, x1, y1, r1, t1, x1, y1, 0, t1);
585 } else {
586 if (Extend1) {
587 r = R_rect_radius(rect, x1, y1);
588 if (r > r1) {
589 code = R_tensor_annulus(pfs, rect, x1, y1, r, t1, x1, y1, r1, t1);
590 if (code < 0)
591 return code;
592 }
593 }
594 if (Extend0 && r0 > 0)
595 return R_tensor_annulus(pfs, rect, x0, y0, r0, t0, x0, y0, 0, t0);
596 }
597 } else if (dr > d / 3) {
598 /* Obtuse cone. */
599 if (r0 > r1) {
600 if (Extend0) {
601 r = R_rect_radius(rect, x0, y0);
602 code = R_obtuse_cone(pfs, rect, x0, y0, r0, x1, y1, r1, t0, r, true);
603 if (code < 0)
604 return code;
605 }
606 if (Extend1 && r1 != 0)
607 return R_tensor_cone_apex(pfs, rect, x0, y0, r0, x1, y1, r1, t1);
608 return 0;
609 } else {
610 if (Extend1) {
611 r = R_rect_radius(rect, x1, y1);
612 code = R_obtuse_cone(pfs, rect, x1, y1, r1, x0, y0, r0, t1, r, false);
613 if (code < 0)
614 return code;
615 }
616 if (Extend0 && r0 != 0)
617 return R_tensor_cone_apex(pfs, rect, x1, y1, r1, x0, y0, r0, t0);
618 }
619 } else {
620 /* Acute cone or cylinder. */
621 double x2, y2, r2, x3, y3, r3;
622
623 if (Extend0) {
624 code = R_outer_circle(pfs, rect, x1, y1, r1, x0, y0, r0, &x3, &y3, &r3);
625 if (code < 0)
626 return code;
627 if (x3 != x1 || y3 != y1) {
628 code = R_tensor_annulus(pfs, rect, x0, y0, r0, t0, x3, y3, r3, t0);
629 if (code < 0)
630 return code;
631 }
632 }
633 if (Extend1) {
634 code = R_outer_circle(pfs, rect, x0, y0, r0, x1, y1, r1, &x2, &y2, &r2);
635 if (code < 0)
636 return code;
637 if (x2 != x0 || y2 != y0) {
638 code = R_tensor_annulus(pfs, rect, x1, y1, r1, t1, x2, y2, r2, t1);
639 if (code < 0)
640 return code;
641 }
642 }
643 }
644 return 0;
645 }
646
647 static int
R_fill_rect_with_const_color(patch_fill_state_t * pfs,const gs_fixed_rect * clip_rect,float t)648 R_fill_rect_with_const_color(patch_fill_state_t *pfs, const gs_fixed_rect *clip_rect, float t)
649 {
650 #if 0 /* Disabled because the clist writer device doesn't pass
651 the clipping path with fill_recatangle. */
652 patch_color_t pc;
653 const gs_color_space *pcs = pfs->direct_space;
654 gx_device_color dc;
655 int code;
656
657 code = gs_function_evaluate(pfs->Function, &t, pc.cc.paint.values);
658 if (code < 0)
659 return code;
660 pcs->type->restrict_color(&pc.cc, pcs);
661 code = patch_color_to_device_color(pfs, &pc, &dc);
662 if (code < 0)
663 return code;
664 return gx_fill_rectangle_device_rop(fixed2int_pixround(clip_rect->p.x), fixed2int_pixround(clip_rect->p.y),
665 fixed2int_pixround(clip_rect->q.x) - fixed2int_pixround(clip_rect->p.x),
666 fixed2int_pixround(clip_rect->q.y) - fixed2int_pixround(clip_rect->p.y),
667 &dc, pfs->dev, pfs->pis->log_op);
668 #else
669 /* Can't apply fill_rectangle, because the clist writer device doesn't pass
670 the clipping path with fill_recatangle. Convert into trapezoids instead.
671 */
672 quadrangle_patch p;
673 shading_vertex_t pp[2][2];
674 const gs_color_space *pcs = pfs->direct_space;
675 patch_color_t pc;
676 int code;
677
678 code = gs_function_evaluate(pfs->Function, &t, pc.cc.paint.values);
679 if (code < 0)
680 return code;
681 pcs->type->restrict_color(&pc.cc, pcs);
682 pc.t[0] = pc.t[1] = t;
683 pp[0][0].p = clip_rect->p;
684 pp[0][1].p.x = clip_rect->q.x;
685 pp[0][1].p.y = clip_rect->p.y;
686 pp[1][0].p.x = clip_rect->p.x;
687 pp[1][0].p.y = clip_rect->q.y;
688 pp[1][1].p = clip_rect->q;
689 pp[0][0].c = pp[0][1].c = pp[1][0].c = pp[1][1].c = &pc;
690 p.p[0][0] = &pp[0][0];
691 p.p[0][1] = &pp[0][1];
692 p.p[1][0] = &pp[1][0];
693 p.p[1][1] = &pp[1][1];
694 return constant_color_quadrangle(pfs, &p, false);
695 #endif
696 }
697
698 typedef struct radial_shading_attrs_s {
699 double x0, y0;
700 double x1, y1;
701 double span[2][2];
702 double apex;
703 bool have_apex;
704 bool have_root[2]; /* ongoing contact, outgoing contact. */
705 bool outer_contact[2];
706 gs_point p[6]; /* 4 corners of the rectangle, p[4] = p[0], p[5] = p[1] */
707 } radial_shading_attrs_t;
708
709 #define Pw2(a) ((a)*(a))
710
711 static void
radial_shading_external_contact(radial_shading_attrs_t * rsa,int point_index,double t,double r0,double r1,bool at_corner,int root_index)712 radial_shading_external_contact(radial_shading_attrs_t *rsa, int point_index, double t, double r0, double r1, bool at_corner, int root_index)
713 {
714 double cx = rsa->x0 + (rsa->x1 - rsa->x0) * t;
715 double cy = rsa->y0 + (rsa->y1 - rsa->y0) * t;
716 double rx = rsa->p[point_index].x - cx;
717 double ry = rsa->p[point_index].y - cy;
718 double dx = rsa->p[point_index - 1].x - rsa->p[point_index].x;
719 double dy = rsa->p[point_index - 1].y - rsa->p[point_index].y;
720
721 if (at_corner) {
722 double Dx = rsa->p[point_index + 1].x - rsa->p[point_index].x;
723 double Dy = rsa->p[point_index + 1].y - rsa->p[point_index].y;
724 bool b1 = (dx * rx + dy * ry >= 0);
725 bool b2 = (Dx * rx + Dy * ry >= 0);
726
727 if (b1 & b2)
728 rsa->outer_contact[root_index] = true;
729 } else {
730 if (rx * dy - ry * dx < 0)
731 rsa->outer_contact[root_index] = true;
732 }
733 }
734
735 static void
store_roots(radial_shading_attrs_t * rsa,const bool have_root[2],const double t[2],double r0,double r1,int point_index,bool at_corner)736 store_roots(radial_shading_attrs_t *rsa, const bool have_root[2], const double t[2], double r0, double r1, int point_index, bool at_corner)
737 {
738 int i;
739
740 for (i = 0; i < 2; i++) {
741 bool good_root;
742
743 if (!have_root[i])
744 continue;
745 good_root = (!rsa->have_apex || (rsa->apex <= 0 || r0 == 0 ? t[i] >= rsa->apex : t[i] <= rsa->apex));
746 if (good_root) {
747 radial_shading_external_contact(rsa, point_index, t[i], r0, r1, at_corner, i);
748 if (!rsa->have_root[i]) {
749 rsa->span[i][0] = rsa->span[i][1] = t[i];
750 rsa->have_root[i] = true;
751 } else {
752 if (rsa->span[i][0] > t[i])
753 rsa->span[i][0] = t[i];
754 if (rsa->span[i][1] < t[i])
755 rsa->span[i][1] = t[i];
756 }
757 }
758 }
759 }
760
761 static void
compute_radial_shading_span_extended_side(radial_shading_attrs_t * rsa,double r0,double r1,int point_index)762 compute_radial_shading_span_extended_side(radial_shading_attrs_t *rsa, double r0, double r1, int point_index)
763 {
764 double cc, c;
765 bool have_root[2] = {false, false};
766 double t[2];
767 bool by_x = (rsa->p[point_index].x == rsa->p[point_index + 1].x);
768 int i;
769
770 /* Assuming x0 = y0 = 0 :
771 cc * t +- (r0 + (r1 - r0) * t) == c
772 t0 := (c - r0) / (cc + (r1 - r0))
773 t1 := (c + r0) / (cc - (r1 - r0))
774 */
775 if (by_x) {
776 c = rsa->p[point_index].x - rsa->x0;
777 cc = rsa->x1 - rsa->x0;
778 } else {
779 c = rsa->p[point_index].y - rsa->y0;
780 cc = rsa->y1 - rsa->y0;
781 }
782 t[0] = (c - r0) / (cc + r1 - r0);
783 t[1] = (c + r0) / (cc - r1 + r0);
784 if (t[0] > t[1]) {
785 t[0] = t[1];
786 t[1] = (c - r0) / (cc + r1 - r0);
787 }
788 for (i = 0; i < 2; i++) {
789 double d, d0, d1;
790
791 if (by_x) {
792 d = rsa->y1 - rsa->y0 + r0 + (r1 - r0) * t[i];
793 d0 = rsa->p[point_index].y;
794 d1 = rsa->p[point_index + 1].y;
795 } else {
796 d = rsa->x1 - rsa->x0 + r0 + (r1 - r0) * t[i];
797 d0 = rsa->p[point_index].x;
798 d1 = rsa->p[point_index + 1].x;
799 }
800 if (d1 > d0 ? d0 <= d && d <= d1 : d1 <= d && d <= d0)
801 have_root[i] = true;
802 }
803 store_roots(rsa, have_root, t, r0, r1, point_index, false);
804 }
805
806 static int
compute_radial_shading_span_extended_point(radial_shading_attrs_t * rsa,double r0,double r1,int point_index)807 compute_radial_shading_span_extended_point(radial_shading_attrs_t *rsa, double r0, double r1, int point_index)
808 {
809 double p1x = rsa->x1 - rsa->x0;
810 double p1y = rsa->y1 - rsa->y0;
811 double qx = rsa->p[point_index].x - rsa->x0;
812 double qy = rsa->p[point_index].y - rsa->y0;
813 double div = (Pw2(p1x) + Pw2(p1y) - Pw2(r0 - r1));
814 bool have_root[2] = {false, false};
815 double t[2];
816
817 if (fabs(div) < 1e-8) {
818 /* Linear equation. */
819 /* This case is always the ongoing eclipese contact. */
820 double cx = rsa->x0 - (rsa->x1 - rsa->x0) * r0 / (r1 - r0);
821 double cy = rsa->y0 - (rsa->y1 - rsa->y0) * r0 / (r1 - r0);
822
823 t[0] = (Pw2(qx) + Pw2(qy))/(cx*qx + cy*qy) / 2;
824 have_root[0] = true;
825 } else {
826 /* Square equation. */
827 double desc2 = -((Pw2(qx) + Pw2(qy) - Pw2(r0))*(Pw2(p1x) + Pw2(p1y) - Pw2(r0 - r1))) + Pw2(p1x*qx + p1y*qy + r0*(-r0 + r1));
828
829 if (desc2 < 0) {
830 return -1; /* The point is outside the shading coverage.
831 Do not shorten, because we didn't observe it in practice. */
832 } else {
833 double desc1 = sqrt(desc2);
834
835 if (div > 0) {
836 t[0] = (p1x*qx + p1y*qy + r0*(-r0 + r1) - desc1) / div;
837 t[1] = (p1x*qx + p1y*qy + r0*(-r0 + r1) + desc1) / div;
838 } else {
839 t[0] = (p1x*qx + p1y*qy + r0*(-r0 + r1) + desc1) / div;
840 t[1] = (p1x*qx + p1y*qy + r0*(-r0 + r1) - desc1) / div;
841 }
842 have_root[0] = have_root[1] = true;
843 }
844 }
845 store_roots(rsa, have_root, t, r0, r1, point_index, true);
846 if (have_root[0] && have_root[1])
847 return 15;
848 if (have_root[0])
849 return 15 - 4;
850 if (have_root[1])
851 return 15 - 2;
852 return -1;
853 }
854
855 #undef Pw2
856
857 static int
compute_radial_shading_span_extended(radial_shading_attrs_t * rsa,double r0,double r1)858 compute_radial_shading_span_extended(radial_shading_attrs_t *rsa, double r0, double r1)
859 {
860 int span_type0, span_type1;
861
862 span_type0 = compute_radial_shading_span_extended_point(rsa, r0, r1, 1);
863 if (span_type0 == -1)
864 return -1;
865 span_type1 = compute_radial_shading_span_extended_point(rsa, r0, r1, 2);
866 if (span_type0 != span_type1)
867 return -1;
868 span_type1 = compute_radial_shading_span_extended_point(rsa, r0, r1, 3);
869 if (span_type0 != span_type1)
870 return -1;
871 span_type1 = compute_radial_shading_span_extended_point(rsa, r0, r1, 4);
872 if (span_type0 != span_type1)
873 return -1;
874 compute_radial_shading_span_extended_side(rsa, r0, r1, 1);
875 compute_radial_shading_span_extended_side(rsa, r0, r1, 2);
876 compute_radial_shading_span_extended_side(rsa, r0, r1, 3);
877 compute_radial_shading_span_extended_side(rsa, r0, r1, 4);
878 return span_type0;
879 }
880
881 static int
compute_radial_shading_span(radial_shading_attrs_t * rsa,float x0,float y0,floatp r0,float x1,float y1,floatp r1,const gs_rect * rect)882 compute_radial_shading_span(radial_shading_attrs_t *rsa, float x0, float y0, floatp r0, float x1, float y1, floatp r1, const gs_rect * rect)
883 {
884 /* If the shading area is much larger than the path bbox,
885 we want to shorten the shading for a faster rendering.
886 If any point of the path bbox falls outside the shading area,
887 our math is not applicable, and we render entire shading.
888 If the path bbox is inside the shading area,
889 we compute 1 or 2 'spans' - the shading parameter intervals,
890 which covers the bbox. For doing that we need to resolve
891 a square eqation by the shading parameter
892 for each corner of the bounding box,
893 and for each side of the shading bbox.
894 Note the equation to be solved in the user space.
895 Since each equation gives 2 roots (because the points are
896 strongly inside the shading area), we will get 2 parameter intervals -
897 the 'lower' one corresponds to the first (ongoing) contact of
898 the running circle, and the second one corresponds to the last (outgoing) contact
899 (like in a sun eclipse; well our sun is rectangular).
900
901 Here are few exceptions.
902
903 First, the equation degenerates when the distance sqrt((x1-x0)^2 + (y1-y0)^2)
904 appears equal to r0-r1. In this case the base circles do contact,
905 and the running circle does contact at the same point.
906 The equation degenerates to a linear one.
907 Since we don't want float precision noize to affect the result,
908 we compute this condition in 'fixed' coordinates.
909
910 Second, Postscript approximates any circle with 3d order beziers.
911 This approximation may give a 2% error.
912 Therefore using the precise roots may cause a dropout.
913 To prevetn them, we slightly modify the base radii.
914 However the sign of modification smartly depends
915 on the relative sizes of the base circles,
916 and on the contact number. Currently we don't want to
917 define and debug the smart optimal logic for that,
918 so we simply try all 4 variants for each source equation,
919 and use the union of intervals.
920
921 Third, we could compute which quarter of the circle
922 really covers the path bbox. Using it we could skip
923 rendering of uncovering quarters. Currently we do not
924 implement this optimization. The general tensor patch algorithm
925 will skip uncovering parts.
926
927 Fourth, when one base circle is (almost) inside the other,
928 the parameter interval must include the shading apex.
929 To know that, we determine whether the contacting circle
930 is outside the rectangle (the "outer" contact),
931 or it is (partially) inside the rectangle.
932
933 At last, a small shortening of a shading won't give a
934 sensible speedup, but it may replace a symmetric function domain
935 with an assymmetric one, so that the rendering
936 would be asymmetyric for a symmetric shading.
937 Therefore we do not perform a small sortening.
938 Instead we shorten only if the shading span
939 is much smaller that the shading domain.
940 */
941 const double extent = 1.02;
942 int span_type0, span_type1, span_type;
943
944 memset(rsa, 0, sizeof(*rsa));
945 rsa->x0 = x0;
946 rsa->y0 = y0;
947 rsa->x1 = x1;
948 rsa->y1 = y1;
949 rsa->p[0] = rsa->p[4] = rect->p;
950 rsa->p[1].x = rsa->p[5].x = rect->p.x;
951 rsa->p[1].y = rsa->p[5].y = rect->q.y;
952 rsa->p[2] = rect->q;
953 rsa->p[3].x = rect->q.x;
954 rsa->p[3].y = rect->p.y;
955 rsa->have_apex = any_abs(r1 - r0) > 1e-7 * any_abs(r1 + r0);
956 rsa->apex = (rsa->have_apex ? -r0 / (r1 - r0) : 0);
957 span_type0 = compute_radial_shading_span_extended(rsa, r0 / extent, r1 * extent);
958 if (span_type0 == -1)
959 return -1;
960 span_type1 = compute_radial_shading_span_extended(rsa, r0 / extent, r1 / extent);
961 if (span_type0 != span_type1)
962 return -1;
963 span_type1 = compute_radial_shading_span_extended(rsa, r0 * extent, r1 * extent);
964 if (span_type0 != span_type1)
965 return -1;
966 span_type1 = compute_radial_shading_span_extended(rsa, r0 * extent, r1 / extent);
967 if (span_type1 == -1)
968 return -1;
969 if (r0 < r1) {
970 if (rsa->have_root[0] && !rsa->outer_contact[0])
971 rsa->span[0][0] = rsa->apex; /* Likely never happens. Remove ? */
972 if (rsa->have_root[1] && !rsa->outer_contact[1])
973 rsa->span[1][0] = rsa->apex;
974 } else if (r0 > r1) {
975 if (rsa->have_root[0] && !rsa->outer_contact[0])
976 rsa->span[0][1] = rsa->apex;
977 if (rsa->have_root[1] && !rsa->outer_contact[1])
978 rsa->span[1][1] = rsa->apex; /* Likely never happens. Remove ? */
979 }
980 span_type = 0;
981 if (rsa->have_root[0] && rsa->span[0][0] < 0)
982 span_type |= 1;
983 if (rsa->have_root[1] && rsa->span[1][0] < 0)
984 span_type |= 1;
985 if (rsa->have_root[0] && rsa->span[0][1] > 0 && rsa->span[0][0] < 1)
986 span_type |= 2;
987 if (rsa->have_root[1] && rsa->span[1][1] > 0 && rsa->span[1][0] < 1)
988 span_type |= 4;
989 if (rsa->have_root[0] && rsa->span[0][1] > 1)
990 span_type |= 8;
991 if (rsa->have_root[1] && rsa->span[1][1] > 1)
992 span_type |= 8;
993 return span_type;
994 }
995
996 static bool
shorten_radial_shading(float * x0,float * y0,floatp * r0,float * d0,float * x1,float * y1,floatp * r1,float * d1,double span_[2])997 shorten_radial_shading(float *x0, float *y0, floatp *r0, float *d0, float *x1, float *y1, floatp *r1, float *d1, double span_[2])
998 {
999 double s0 = span_[0], s1 = span_[1], w;
1000
1001 if (s0 < 0)
1002 s0 = 0;
1003 if (s1 < 0)
1004 s1 = 0;
1005 if (s0 > 1)
1006 s0 = 1;
1007 if (s1 > 1)
1008 s1 = 1;
1009 w = s1 - s0;
1010 if (w == 0)
1011 return false; /* Don't pass a degenerate shading. */
1012 if (w > 0.3)
1013 return false; /* The span is big, don't shorten it. */
1014 { /* Do shorten. */
1015 double R0 = *r0, X0 = *x0, Y0 = *y0, D0 = *d0;
1016 double R1 = *r1, X1 = *x1, Y1 = *y1, D1 = *d1;
1017
1018 *r0 = R0 + (R1 - R0) * s0;
1019 *x0 = X0 + (X1 - X0) * s0;
1020 *y0 = Y0 + (Y1 - Y0) * s0;
1021 *d0 = D0 + (D1 - D0) * s0;
1022 *r1 = R0 + (R1 - R0) * s1;
1023 *x1 = X0 + (X1 - X0) * s1;
1024 *y1 = Y0 + (Y1 - Y0) * s1;
1025 *d1 = D0 + (D1 - D0) * s1;
1026 }
1027 return true;
1028 }
1029
1030 static bool inline
is_radial_shading_large(double x0,double y0,double r0,double d0,double x1,double y1,double r1,const gs_rect * rect)1031 is_radial_shading_large(double x0, double y0, double r0, double d0, double x1, double y1, double r1, const gs_rect * rect)
1032 {
1033 const double d = hypot(x1 - x0, y1 - y0);
1034 const double area0 = M_PI * r0 * r0 / 2;
1035 const double area1 = M_PI * r1 * r1 / 2;
1036 const double area2 = (r0 + r1) / 2 * d;
1037 const double arbitrary = 8;
1038 double areaX, areaY;
1039
1040 /* The shading area is not equal to area0 + area1 + area2
1041 when one circle is (almost) inside the other.
1042 We believe that the 'arbitrary' coefficient recovers that
1043 when it is set greater than 2. */
1044 /* If one dimension is large enough, the shading parameter span is wide. */
1045 areaX = (rect->q.x - rect->p.x) * (rect->q.x - rect->p.x);
1046 if (areaX * arbitrary < area0 + area1 + area2)
1047 return true;
1048 areaY = (rect->q.y - rect->p.y) * (rect->q.y - rect->p.y);
1049 if (areaY * arbitrary < area0 + area1 + area2)
1050 return true;
1051 return false;
1052 }
1053
1054 static int
gs_shading_R_fill_rectangle_aux(const gs_shading_t * psh0,const gs_rect * rect,const gs_fixed_rect * clip_rect,gx_device * dev,gs_imager_state * pis)1055 gs_shading_R_fill_rectangle_aux(const gs_shading_t * psh0, const gs_rect * rect,
1056 const gs_fixed_rect *clip_rect,
1057 gx_device * dev, gs_imager_state * pis)
1058 {
1059 const gs_shading_R_t *const psh = (const gs_shading_R_t *)psh0;
1060 float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1];
1061 float x0 = psh->params.Coords[0], y0 = psh->params.Coords[1];
1062 floatp r0 = psh->params.Coords[2];
1063 float x1 = psh->params.Coords[3], y1 = psh->params.Coords[4];
1064 floatp r1 = psh->params.Coords[5];
1065 radial_shading_attrs_t rsa;
1066 int span_type; /* <0 - don't shorten, 1 - extent0, 2 - first contact, 4 - last contact, 8 - extent1. */
1067 int code;
1068 patch_fill_state_t pfs1;
1069
1070 if (r0 == 0 && r1 == 0)
1071 return 0; /* PLRM requires to paint nothing. */
1072 shade_init_fill_state((shading_fill_state_t *)&pfs1, psh0, dev, pis);
1073 pfs1.Function = psh->params.Function;
1074 code = init_patch_fill_state(&pfs1);
1075 if (code < 0)
1076 return code;
1077 pfs1.function_arg_shift = 1;
1078 pfs1.rect = *clip_rect;
1079 pfs1.maybe_self_intersecting = false;
1080 if (is_radial_shading_large(x0, y0, r0, d0, x1, y1, r1, rect))
1081 span_type = compute_radial_shading_span(&rsa, x0, y0, r0, x1, y1, r1, rect);
1082 else
1083 span_type = -1;
1084 if (span_type < 0) {
1085 code = R_extensions(&pfs1, psh, rect, d0, d1, psh->params.Extend[0], false);
1086 if (code >= 0)
1087 code = R_tensor_annulus(&pfs1, rect, x0, y0, r0, d0, x1, y1, r1, d1);
1088 if (code >= 0)
1089 code = R_extensions(&pfs1, psh, rect, d0, d1, false, psh->params.Extend[1]);
1090 } else if (span_type == 1) {
1091 code = R_fill_rect_with_const_color(&pfs1, clip_rect, d0);
1092 } else if (span_type == 8) {
1093 code = R_fill_rect_with_const_color(&pfs1, clip_rect, d1);
1094 } else {
1095 bool second_interval = true;
1096
1097 code = 0;
1098 if (span_type & 1)
1099 code = R_extensions(&pfs1, psh, rect, d0, d1, psh->params.Extend[0], false);
1100 if (code >= 0) {
1101 float X0 = x0, Y0 = y0, D0 = d0, X1 = x1, Y1 = y1, D1 = d1;
1102 floatp R0 = r0, R1 = r1;
1103
1104 if ((span_type & 2) && (span_type & 4) && rsa.span[0][1] >= rsa.span[1][0]) {
1105 double united[2];
1106
1107 united[0] = rsa.span[0][0];
1108 united[1] = rsa.span[1][1];
1109 shorten_radial_shading(&X0, &Y0, &R0, &D0, &X1, &Y1, &R1, &D1, united);
1110 second_interval = false;
1111 code = R_tensor_annulus(&pfs1, rect, X0, Y0, R0, D0, X1, Y1, R1, D1);
1112 } else if (span_type & 2) {
1113 second_interval = shorten_radial_shading(&X0, &Y0, &R0, &D0, &X1, &Y1, &R1, &D1, rsa.span[0]);
1114 code = R_tensor_annulus(&pfs1, rect, X0, Y0, R0, D0, X1, Y1, R1, D1);
1115 }
1116 }
1117 if (code >= 0 && second_interval) {
1118 if (span_type & 4) {
1119 float X0 = x0, Y0 = y0, D0 = d0, X1 = x1, Y1 = y1, D1 = d1;
1120 floatp R0 = r0, R1 = r1;
1121
1122 shorten_radial_shading(&X0, &Y0, &R0, &D0, &X1, &Y1, &R1, &D1, rsa.span[1]);
1123 code = R_tensor_annulus(&pfs1, rect, X0, Y0, R0, D0, X1, Y1, R1, D1);
1124 }
1125 }
1126 if (code >= 0 && (span_type & 8))
1127 code = R_extensions(&pfs1, psh, rect, d0, d1, false, psh->params.Extend[1]);
1128 }
1129 if (term_patch_fill_state(&pfs1))
1130 return_error(gs_error_unregistered); /* Must not happen. */
1131 return code;
1132 }
1133
1134 int
gs_shading_R_fill_rectangle(const gs_shading_t * psh0,const gs_rect * rect,const gs_fixed_rect * rect_clip,gx_device * dev,gs_imager_state * pis)1135 gs_shading_R_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
1136 const gs_fixed_rect * rect_clip,
1137 gx_device * dev, gs_imager_state * pis)
1138 {
1139 int code;
1140
1141 if (VD_TRACE_RADIAL_PATCH && vd_allowed('s')) {
1142 vd_get_dc('s');
1143 vd_set_shift(0, 0);
1144 vd_set_scale(0.01);
1145 vd_set_origin(0, 0);
1146 }
1147 code = gs_shading_R_fill_rectangle_aux(psh0, rect, rect_clip, dev, pis);
1148 if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s'))
1149 vd_release_dc;
1150 return code;
1151 }
1152