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