xref: /plan9/sys/src/cmd/gs/src/gxshade1.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gxshade1.c,v 1.41 2005/05/25 15:57:58 igor Exp $ */
18 /* Rendering for non-mesh shadings */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsmatrix.h"		/* for gscoord.h */
24 #include "gscoord.h"
25 #include "gspath.h"
26 #include "gsptype2.h"
27 #include "gxcspace.h"
28 #include "gxdcolor.h"
29 #include "gxfarith.h"
30 #include "gxfixed.h"
31 #include "gxistate.h"
32 #include "gxpath.h"
33 #include "gxshade.h"
34 #include "gxshade4.h"
35 #include "gxdevcli.h"
36 #include "vdtrace.h"
37 #include <assert.h>
38 
39 #define VD_TRACE_AXIAL_PATCH 1
40 #define VD_TRACE_RADIAL_PATCH 1
41 #define VD_TRACE_FUNCTIONAL_PATCH 1
42 
43 
44 /* ---------------- Function-based shading ---------------- */
45 
46 typedef struct Fb_frame_s {	/* A rudiment of old code. */
47     gs_rect region;
48     gs_client_color cc[4];	/* colors at 4 corners */
49     int state;
50 } Fb_frame_t;
51 
52 typedef struct Fb_fill_state_s {
53     shading_fill_state_common;
54     const gs_shading_Fb_t *psh;
55     gs_matrix_fixed ptm;	/* parameter space -> device space */
56     Fb_frame_t frame;
57 } Fb_fill_state_t;
58 /****** NEED GC DESCRIPTOR ******/
59 
60 private inline void
make_other_poles(patch_curve_t curve[4])61 make_other_poles(patch_curve_t curve[4])
62 {
63     int i, j;
64 
65     for (i = 0; i < 4; i++) {
66 	j = (i + 1) % 4;
67 	curve[i].control[0].x = (curve[i].vertex.p.x * 2 + curve[j].vertex.p.x);
68 	curve[i].control[0].x /= 3;
69 	curve[i].control[0].y = (curve[i].vertex.p.y * 2 + curve[j].vertex.p.y);
70 	curve[i].control[0].y /= 3;
71 	curve[i].control[1].x = (curve[i].vertex.p.x + curve[j].vertex.p.x * 2);
72 	curve[i].control[1].y /= 3;
73 	curve[i].control[1].y = (curve[i].vertex.p.y + curve[j].vertex.p.y * 2);
74 	curve[i].control[1].y /= 3;
75 	curve[i].straight = true;
76     }
77 }
78 
79 private int
Fb_fill_region(Fb_fill_state_t * pfs,const gs_fixed_rect * rect)80 Fb_fill_region(Fb_fill_state_t * pfs, const gs_fixed_rect *rect)
81 {
82     patch_fill_state_t pfs1;
83     patch_curve_t curve[4];
84     Fb_frame_t * fp = &pfs->frame;
85     int code;
86 
87     if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s')) {
88 	vd_get_dc('s');
89 	vd_set_shift(0, 0);
90 	vd_set_scale(0.01);
91 	vd_set_origin(0, 0);
92     }
93     memcpy(&pfs1, (shading_fill_state_t *)pfs, sizeof(shading_fill_state_t));
94     pfs1.Function = pfs->psh->params.Function;
95     code = init_patch_fill_state(&pfs1);
96     if (code < 0)
97 	return code;
98     pfs1.maybe_self_intersecting = false;
99     pfs1.n_color_args = 2;
100     pfs1.rect = *rect;
101     gs_point_transform2fixed(&pfs->ptm, fp->region.p.x, fp->region.p.y, &curve[0].vertex.p);
102     gs_point_transform2fixed(&pfs->ptm, fp->region.q.x, fp->region.p.y, &curve[1].vertex.p);
103     gs_point_transform2fixed(&pfs->ptm, fp->region.q.x, fp->region.q.y, &curve[2].vertex.p);
104     gs_point_transform2fixed(&pfs->ptm, fp->region.p.x, fp->region.q.y, &curve[3].vertex.p);
105     make_other_poles(curve);
106     curve[0].vertex.cc[0] = fp->region.p.x;   curve[0].vertex.cc[1] = fp->region.p.y;
107     curve[1].vertex.cc[0] = fp->region.q.x;   curve[1].vertex.cc[1] = fp->region.p.y;
108     curve[2].vertex.cc[0] = fp->region.q.x;   curve[2].vertex.cc[1] = fp->region.q.y;
109     curve[3].vertex.cc[0] = fp->region.p.x;   curve[3].vertex.cc[1] = fp->region.q.y;
110     code = patch_fill(&pfs1, curve, NULL, NULL);
111     term_patch_fill_state(&pfs1);
112     if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s'))
113 	vd_release_dc;
114     return code;
115 }
116 
117 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)118 gs_shading_Fb_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
119 			     const gs_fixed_rect * rect_clip,
120 			     gx_device * dev, gs_imager_state * pis)
121 {
122     const gs_shading_Fb_t * const psh = (const gs_shading_Fb_t *)psh0;
123     gs_matrix save_ctm;
124     int xi, yi;
125     float x[2], y[2];
126     Fb_fill_state_t state;
127 
128     shade_init_fill_state((shading_fill_state_t *) & state, psh0, dev, pis);
129     state.psh = psh;
130     /****** HACK FOR FIXED-POINT MATRIX MULTIPLY ******/
131     gs_currentmatrix((gs_state *) pis, &save_ctm);
132     gs_concat((gs_state *) pis, &psh->params.Matrix);
133     state.ptm = pis->ctm;
134     gs_setmatrix((gs_state *) pis, &save_ctm);
135     /* Compute the parameter X and Y ranges. */
136     {
137 	gs_rect pbox;
138 
139 	gs_bbox_transform_inverse(rect, &psh->params.Matrix, &pbox);
140 	x[0] = max(pbox.p.x, psh->params.Domain[0]);
141 	x[1] = min(pbox.q.x, psh->params.Domain[1]);
142 	y[0] = max(pbox.p.y, psh->params.Domain[2]);
143 	y[1] = min(pbox.q.y, psh->params.Domain[3]);
144     }
145     for (xi = 0; xi < 2; ++xi)
146 	for (yi = 0; yi < 2; ++yi) {
147 	    float v[2];
148 
149 	    v[0] = x[xi], v[1] = y[yi];
150 	    gs_function_evaluate(psh->params.Function, v,
151 				 state.frame.cc[yi * 2 + xi].paint.values);
152 	}
153     state.frame.region.p.x = x[0];
154     state.frame.region.p.y = y[0];
155     state.frame.region.q.x = x[1];
156     state.frame.region.q.y = y[1];
157     return Fb_fill_region(&state, rect_clip);
158 }
159 
160 /* ---------------- Axial shading ---------------- */
161 
162 typedef struct A_fill_state_s {
163     shading_fill_state_common;
164     const gs_shading_A_t *psh;
165     gs_rect rect;		/* bounding rectangle in user space */
166     gs_point delta;
167     double length;
168     double t0, t1;
169     double v0, v1, u0, u1;
170 } A_fill_state_t;
171 /****** NEED GC DESCRIPTOR ******/
172 
173 /* Note t0 and t1 vary over [0..1], not the Domain. */
174 
175 private int
A_fill_region(A_fill_state_t * pfs,const gs_fixed_rect * rect_clip)176 A_fill_region(A_fill_state_t * pfs, const gs_fixed_rect *rect_clip)
177 {
178     const gs_shading_A_t * const psh = pfs->psh;
179     gs_function_t * const pfn = psh->params.Function;
180     double x0 = psh->params.Coords[0] + pfs->delta.x * pfs->v0;
181     double y0 = psh->params.Coords[1] + pfs->delta.y * pfs->v0;
182     double x1 = psh->params.Coords[0] + pfs->delta.x * pfs->v1;
183     double y1 = psh->params.Coords[1] + pfs->delta.y * pfs->v1;
184     double h0 = pfs->u0, h1 = pfs->u1;
185     patch_curve_t curve[4];
186     patch_fill_state_t pfs1;
187     int code;
188 
189     memcpy(&pfs1, (shading_fill_state_t *)pfs, sizeof(shading_fill_state_t));
190     pfs1.Function = pfn;
191     code = init_patch_fill_state(&pfs1);
192     if (code < 0)
193 	return code;
194     pfs1.rect = *rect_clip;
195     pfs1.maybe_self_intersecting = false;
196     gs_point_transform2fixed(&pfs->pis->ctm, x0 + pfs->delta.y * h0, y0 - pfs->delta.x * h0, &curve[0].vertex.p);
197     gs_point_transform2fixed(&pfs->pis->ctm, x1 + pfs->delta.y * h0, y1 - pfs->delta.x * h0, &curve[1].vertex.p);
198     gs_point_transform2fixed(&pfs->pis->ctm, x1 + pfs->delta.y * h1, y1 - pfs->delta.x * h1, &curve[2].vertex.p);
199     gs_point_transform2fixed(&pfs->pis->ctm, x0 + pfs->delta.y * h1, y0 - pfs->delta.x * h1, &curve[3].vertex.p);
200     curve[0].vertex.cc[0] = curve[0].vertex.cc[1] = pfs->t0; /* The element cc[1] is set to a dummy value against */
201     curve[1].vertex.cc[0] = curve[1].vertex.cc[1] = pfs->t1; /* interrupts while an idle priocessing in gxshade.6.c .  */
202     curve[2].vertex.cc[0] = curve[2].vertex.cc[1] = pfs->t1;
203     curve[3].vertex.cc[0] = curve[3].vertex.cc[1] = pfs->t0;
204     make_other_poles(curve);
205     code = patch_fill(&pfs1, curve, NULL, NULL);
206     term_patch_fill_state(&pfs1);
207     return code;
208 }
209 
210 private 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)211 gs_shading_A_fill_rectangle_aux(const gs_shading_t * psh0, const gs_rect * rect,
212 			    const gs_fixed_rect *clip_rect,
213 			    gx_device * dev, gs_imager_state * pis)
214 {
215     const gs_shading_A_t *const psh = (const gs_shading_A_t *)psh0;
216     gs_matrix cmat;
217     gs_rect t_rect;
218     A_fill_state_t state;
219     float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1];
220     float dd = d1 - d0;
221     double t0, t1;
222     gs_point dist;
223     int code = 0;
224 
225     shade_init_fill_state((shading_fill_state_t *)&state, psh0, dev, pis);
226     state.psh = psh;
227     state.rect = *rect;
228     /*
229      * Compute the parameter range.  We construct a matrix in which
230      * (0,0) corresponds to t = 0 and (0,1) corresponds to t = 1,
231      * and use it to inverse-map the rectangle to be filled.
232      */
233     cmat.tx = psh->params.Coords[0];
234     cmat.ty = psh->params.Coords[1];
235     state.delta.x = psh->params.Coords[2] - psh->params.Coords[0];
236     state.delta.y = psh->params.Coords[3] - psh->params.Coords[1];
237     cmat.yx = state.delta.x;
238     cmat.yy = state.delta.y;
239     cmat.xx = cmat.yy;
240     cmat.xy = -cmat.yx;
241     gs_bbox_transform_inverse(rect, &cmat, &t_rect);
242     t0 = min(max(t_rect.p.y, 0), 1);
243     t1 = max(min(t_rect.q.y, 1), 0);
244     state.v0 = t0;
245     state.v1 = t1;
246     state.u0 = t_rect.p.x;
247     state.u1 = t_rect.q.x;
248     state.t0 = t0 * dd + d0;
249     state.t1 = t1 * dd + d0;
250     gs_distance_transform(state.delta.x, state.delta.y, &ctm_only(pis),
251 			  &dist);
252     state.length = hypot(dist.x, dist.y);	/* device space line length */
253     code = A_fill_region(&state, clip_rect);
254     if (psh->params.Extend[0] && t0 > t_rect.p.y) {
255 	if (code < 0)
256 	    return code;
257 	/* Use the general algorithm, because we need the trapping. */
258 	state.v0 = t_rect.p.y;
259 	state.v1 = t0;
260 	state.t0 = state.t1 = t0 * dd + d0;
261 	code = A_fill_region(&state, clip_rect);
262     }
263     if (psh->params.Extend[1] && t1 < t_rect.q.y) {
264 	if (code < 0)
265 	    return code;
266 	/* Use the general algorithm, because we need the trapping. */
267 	state.v0 = t1;
268 	state.v1 = t_rect.q.y;
269 	state.t0 = state.t1 = t1 * dd + d0;
270 	code = A_fill_region(&state, clip_rect);
271     }
272     return code;
273 }
274 
275 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)276 gs_shading_A_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
277 			    const gs_fixed_rect * rect_clip,
278 			    gx_device * dev, gs_imager_state * pis)
279 {
280     int code;
281 
282     if (VD_TRACE_AXIAL_PATCH && vd_allowed('s')) {
283 	vd_get_dc('s');
284 	vd_set_shift(0, 0);
285 	vd_set_scale(0.01);
286 	vd_set_origin(0, 0);
287     }
288     code = gs_shading_A_fill_rectangle_aux(psh0, rect, rect_clip, dev, pis);
289     if (VD_TRACE_AXIAL_PATCH && vd_allowed('s'))
290 	vd_release_dc;
291     return code;
292 }
293 
294 /* ---------------- Radial shading ---------------- */
295 
296 typedef struct R_frame_s {	/* A rudiment of old code. */
297     double t0, t1;
298     gs_client_color cc[2];	/* color at t0, t1 */
299 } R_frame_t;
300 
301 typedef struct R_fill_state_s {
302     shading_fill_state_common;
303     const gs_shading_R_t *psh;
304     gs_rect rect;
305     gs_point delta;
306     double dr, dd;
307     R_frame_t frame;
308 } R_fill_state_t;
309 /****** NEED GC DESCRIPTOR ******/
310 
311 private 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)312 R_tensor_annulus(patch_fill_state_t *pfs, const gs_rect *rect,
313     double x0, double y0, double r0, double t0,
314     double x1, double y1, double r1, double t1)
315 {
316     double dx = x1 - x0, dy = y1 - y0;
317     double d = hypot(dx, dy);
318     gs_point p0, p1, pc0, pc1;
319     int k, j, code;
320     bool inside = 0;
321 
322     pc0.x = x0, pc0.y = y0;
323     pc1.x = x1, pc1.y = y1;
324     if (r0 + d <= r1 || r1 + d <= r0) {
325 	/* One circle is inside another one.
326 	   Use any subdivision,
327 	   but don't depend on dx, dy, which may be too small. */
328 	p0.x = 0, p0.y = -1;
329 	/* Align stripes along radii for faster triangulation : */
330 	inside = 1;
331     } else {
332         /* Must generate canonic quadrangle arcs,
333 	   because we approximate them with curves. */
334 	if(any_abs(dx) >= any_abs(dy)) {
335 	    if (dx > 0)
336 		p0.x = 0, p0.y = -1;
337 	    else
338 		p0.x = 0, p0.y = 1;
339 	} else {
340 	    if (dy > 0)
341 		p0.x = 1, p0.y = 0;
342 	    else
343 		p0.x = -1, p0.y = 0;
344 	}
345     }
346     /* fixme: wish: cut invisible parts off.
347        Note : when r0 != r1 the invisible part is not a half circle. */
348     for (k = 0; k < 4; k++, p0 = p1) {
349 	gs_point p[12];
350 	patch_curve_t curve[4];
351 
352 	p1.x = -p0.y; p1.y = p0.x;
353 	if ((k & 1) == k >> 1) {
354 	    make_quadrant_arc(p + 0, &pc0, &p1, &p0, r0);
355 	    make_quadrant_arc(p + 6, &pc1, &p0, &p1, r1);
356 	} else {
357 	    make_quadrant_arc(p + 0, &pc0, &p0, &p1, r0);
358 	    make_quadrant_arc(p + 6, &pc1, &p1, &p0, r1);
359 	}
360 	p[4].x = (p[3].x * 2 + p[6].x) / 3;
361 	p[4].y = (p[3].y * 2 + p[6].y) / 3;
362 	p[5].x = (p[3].x + p[6].x * 2) / 3;
363 	p[5].y = (p[3].y + p[6].y * 2) / 3;
364 	p[10].x = (p[9].x * 2 + p[0].x) / 3;
365 	p[10].y = (p[9].y * 2 + p[0].y) / 3;
366 	p[11].x = (p[9].x + p[0].x * 2) / 3;
367 	p[11].y = (p[9].y + p[0].y * 2) / 3;
368 	for (j = 0; j < 4; j++) {
369 	    int jj = (j + inside) % 4;
370 
371 	    code = gs_point_transform2fixed(&pfs->pis->ctm,
372 			p[j * 3 + 0].x, p[j * 3 + 0].y, &curve[jj].vertex.p);
373 	    if (code < 0)
374 		return code;
375 	    code = gs_point_transform2fixed(&pfs->pis->ctm,
376 			p[j * 3 + 1].x, p[j * 3 + 1].y, &curve[jj].control[0]);
377 	    if (code < 0)
378 		return code;
379 	    code = gs_point_transform2fixed(&pfs->pis->ctm,
380 			p[j * 3 + 2].x, p[j * 3 + 2].y, &curve[jj].control[1]);
381 	    if (code < 0)
382 		return code;
383 	    curve[j].straight = (((j + inside) & 1) != 0);
384 	}
385 	curve[(0 + inside) % 4].vertex.cc[0] = t0;
386 	curve[(1 + inside) % 4].vertex.cc[0] = t0;
387 	curve[(2 + inside) % 4].vertex.cc[0] = t1;
388 	curve[(3 + inside) % 4].vertex.cc[0] = t1;
389 	curve[0].vertex.cc[1] = curve[1].vertex.cc[1] = 0; /* Initialize against FPE. */
390 	curve[2].vertex.cc[1] = curve[3].vertex.cc[1] = 0; /* Initialize against FPE. */
391 	code = patch_fill(pfs, curve, NULL, NULL);
392 	if (code < 0)
393 	    return code;
394     }
395     return 0;
396 }
397 
398 
399 private void
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)400 R_outer_circle(patch_fill_state_t *pfs, const gs_rect *rect,
401 	double x0, double y0, double r0,
402 	double x1, double y1, double r1,
403 	double *x2, double *y2, double *r2)
404 {
405     double dx = x1 - x0, dy = y1 - y0;
406     double sp, sq, s;
407 
408     /* Compute a cone circle, which contacts the rect externally. */
409     /* Don't bother with all 4 sides of the rect,
410        just do with the X or Y span only,
411        so it's not an exact contact, sorry. */
412     if (any_abs(dx) > any_abs(dy)) {
413 	/* Solving :
414 	    x0 + (x1 - x0) * s - r0 - (r1 - r0) * s == bbox_x
415 	    (x1 - x0) * s - (r1 - r0) * s == bbox_x - x0 + r0
416 	    s = (bbox_x - x0 + r0) / (x1 - x0 - r1 + r0)
417 	 */
418 	assert(x1 - x0 + r1 - r0); /* We checked for obtuse cone. */
419 	sp = (rect->p.x - x0 + r0) / (x1 - x0 - r1 + r0);
420 	sq = (rect->q.x - x0 + r0) / (x1 - x0 - r1 + r0);
421     } else {
422 	/* Same by Y. */
423 	sp = (rect->p.y - y0 + r0) / (y1 - y0 - r1 + r0);
424 	sq = (rect->q.y - y0 + r0) / (y1 - y0 - r1 + r0);
425     }
426     if (sp >= 1 && sq >= 1)
427 	s = min(sp, sq);
428     else if(sp >= 1)
429 	s = sp;
430     else if (sq >= 1)
431 	s = sq;
432     else {
433 	/* The circle 1 is outside the rect, use it. */
434         s = 1;
435     }
436     if (r0 + (r1 - r0) * s < 0) {
437 	/* Passed the cone apex, use the apex. */
438 	s = r0 / (r0 - r1);
439 	*r2 = 0;
440     } else
441 	*r2 = r0 + (r1 - r0) * s;
442     *x2 = x0 + (x1 - x0) * s;
443     *y2 = y0 + (y1 - y0) * s;
444 }
445 
446 private double
R_rect_radius(const gs_rect * rect,double x0,double y0)447 R_rect_radius(const gs_rect *rect, double x0, double y0)
448 {
449     double d, dd;
450 
451     dd = hypot(rect->p.x - x0, rect->p.y - y0);
452     d = hypot(rect->p.x - x0, rect->q.y - y0);
453     dd = max(dd, d);
454     d = hypot(rect->q.x - x0, rect->q.y - y0);
455     dd = max(dd, d);
456     d = hypot(rect->q.x - x0, rect->p.y - y0);
457     dd = max(dd, d);
458     return dd;
459 }
460 
461 private 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)462 R_fill_triangle_new(patch_fill_state_t *pfs, const gs_rect *rect,
463     double x0, double y0, double x1, double y1, double x2, double y2, double t)
464 {
465     shading_vertex_t p0, p1, p2;
466     int code;
467 
468     code = gs_point_transform2fixed(&pfs->pis->ctm, x0, y0, &p0.p);
469     if (code < 0)
470 	return code;
471     code = gs_point_transform2fixed(&pfs->pis->ctm, x1, y1, &p1.p);
472     if (code < 0)
473 	return code;
474     code = gs_point_transform2fixed(&pfs->pis->ctm, x2, y2, &p2.p);
475     if (code < 0)
476 	return code;
477     p0.c.t[0] = p0.c.t[1] = t;
478     p1.c.t[0] = p1.c.t[1] = t;
479     p2.c.t[0] = p2.c.t[1] = t;
480     patch_resolve_color(&p0.c, pfs);
481     patch_resolve_color(&p1.c, pfs);
482     patch_resolve_color(&p2.c, pfs);
483     return mesh_triangle(pfs, &p0, &p1, &p2);
484 }
485 
486 private bool
R_is_covered(double ax,double ay,const gs_point * p0,const gs_point * p1,const gs_point * p)487 R_is_covered(double ax, double ay,
488 	const gs_point *p0, const gs_point *p1, const gs_point *p)
489 {
490     double dx0 = p0->x - ax, dy0 = p0->y - ay;
491     double dx1 = p1->x - ax, dy1 = p1->y - ay;
492     double dx = p->x - ax, dy = p->y - ay;
493     double vp0 = dx0 * dy - dy0 * dx;
494     double vp1 = dx * dy1 - dy * dx1;
495 
496     return vp0 >= 0 && vp1 >= 0;
497 }
498 
499 private 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 t1,double r)500 R_obtuse_cone(patch_fill_state_t *pfs, const gs_rect *rect,
501 	double x0, double y0, double r0,
502 	double x1, double y1, double r1, double t1, double r)
503 {
504     double dx = x1 - x0, dy = y1 - y0, dr = any_abs(r1 - r0);
505     double d = hypot(dx, dy);
506     double ax, ay, as; /* Cone apex. */
507     gs_point p0, p1; /* Tangent limits. */
508     gs_point cp[4]; /* Corners.. */
509     gs_point rp[4]; /* Covered corners.. */
510     gs_point pb;
511     int rp_count = 0, cp_start, i, code;
512     bool covered[4];
513 
514     as = r0 / (r0 - r1);
515     ax = x0 + (x1 - x0) * as;
516     ay = y0 + (y1 - y0) * as;
517 
518     if (any_abs(d - dr) < 1e-7 * (d + dr)) {
519 	/* Nearly degenerate, replace with half-plane. */
520 	p0.x = ax - dy * r / d;
521 	p0.y = ay + dx * r / d;
522 	p1.x = ax + dy * r / d;
523 	p1.y = ay - dx * r / d;
524     } else {
525 	/* Tangent limits by proportional triangles. */
526 	double da = hypot(ax - x0, ay - y0);
527 	double h = r * r0 / da, g;
528 
529 	assert(h <= r);
530 	g = sqrt(r * r - h * h);
531 	p0.x = ax - dx * g / d - dy * h / d;
532 	p0.y = ay - dy * g / d + dx * h / d;
533 	p1.x = ax - dx * g / d + dy * h / d;
534 	p1.y = ay - dy * g / d - dx * h / d;
535     }
536     /* Now we have 2 limited tangents, and 4 corners of the rect.
537        Need to know what corners are covered. */
538     cp[0].x = rect->p.x, cp[0].y = rect->p.y;
539     cp[1].x = rect->q.x, cp[1].y = rect->p.y;
540     cp[2].x = rect->q.x, cp[2].y = rect->q.y;
541     cp[3].x = rect->p.x, cp[3].y = rect->q.y;
542     covered[0] = R_is_covered(ax, ay, &p0, &p1, &cp[0]);
543     covered[1] = R_is_covered(ax, ay, &p0, &p1, &cp[1]);
544     covered[2] = R_is_covered(ax, ay, &p0, &p1, &cp[2]);
545     covered[3] = R_is_covered(ax, ay, &p0, &p1, &cp[3]);
546     if (!covered[0] && !covered[1] && !covered[2] && !covered[3]) {
547 	return R_fill_triangle_new(pfs, rect, ax, ay, p0.x, p0.y, p1.x, p1.y, t1);
548     }
549     if (!covered[0] && covered[1])
550 	cp_start = 1;
551     else if (!covered[1] && covered[2])
552 	cp_start = 2;
553     else if (!covered[2] && covered[3])
554 	cp_start = 3;
555     else if (!covered[3] && covered[0])
556 	cp_start = 0;
557     else {
558 	/* Must not happen, handle somehow for safety. */
559 	cp_start = 0;
560     }
561     for (i = cp_start; i < cp_start + 4 && covered[i % 4]; i++) {
562 	rp[rp_count] = cp[i % 4];
563 	rp_count++;
564     }
565     /* Do paint. */
566     pb = p0;
567     for (i = 0; i < rp_count; i++) {
568 	code = R_fill_triangle_new(pfs, rect, ax, ay, pb.x, pb.y, rp[i].x, rp[i].y, t1);
569 	if (code < 0)
570 	    return code;
571 	pb = rp[i];
572     }
573     return R_fill_triangle_new(pfs, rect, ax, ay, pb.x, pb.y, p1.x, p1.y, t1);
574 }
575 
576 private 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)577 R_tensor_cone_apex(patch_fill_state_t *pfs, const gs_rect *rect,
578 	double x0, double y0, double r0,
579 	double x1, double y1, double r1, double t)
580 {
581     double as = r0 / (r0 - r1);
582     double ax = x0 + (x1 - x0) * as;
583     double ay = y0 + (y1 - y0) * as;
584 
585     return R_tensor_annulus(pfs, rect, x1, y1, r1, t, ax, ay, 0, t);
586 }
587 
588 
589 private 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)590 R_extensions(patch_fill_state_t *pfs, const gs_shading_R_t *psh, const gs_rect *rect,
591 	double t0, double t1, bool Extend0, bool Extend1)
592 {
593     float x0 = psh->params.Coords[0], y0 = psh->params.Coords[1];
594     floatp r0 = psh->params.Coords[2];
595     float x1 = psh->params.Coords[3], y1 = psh->params.Coords[4];
596     floatp r1 = psh->params.Coords[5];
597     double dx = x1 - x0, dy = y1 - y0, dr = any_abs(r1 - r0);
598     double d = hypot(dx, dy), r;
599     int code;
600 
601     if (dr >= d - 1e-7 * (d + dr)) {
602 	/* Nested circles, or degenerate. */
603 	if (r0 > r1) {
604 	    if (Extend0) {
605 		r = R_rect_radius(rect, x0, y0);
606 		if (r > r0) {
607 		    code = R_tensor_annulus(pfs, rect, x0, y0, r, t0, x0, y0, r0, t0);
608 		    if (code < 0)
609 			return code;
610 		}
611 	    }
612 	    if (Extend1 && r1 > 0)
613 		return R_tensor_annulus(pfs, rect, x1, y1, r1, t1, x1, y1, 0, t1);
614 	} else {
615 	    if (Extend1) {
616 		r = R_rect_radius(rect, x1, y1);
617 		if (r > r1) {
618 		    code = R_tensor_annulus(pfs, rect, x1, y1, r, t1, x1, y1, r1, t1);
619 		    if (code < 0)
620 			return code;
621 		}
622 	    }
623 	    if (Extend0 && r0 > 0)
624 		return R_tensor_annulus(pfs, rect, x0, y0, r0, t0, x0, y0, 0, t0);
625 	}
626     } else if (dr > d / 3) {
627 	/* Obtuse cone. */
628 	if (r0 > r1) {
629 	    if (Extend0) {
630 		r = R_rect_radius(rect, x0, y0);
631 		code = R_obtuse_cone(pfs, rect, x0, y0, r0, x1, y1, r1, t0, r);
632 		if (code < 0)
633 		    return code;
634 	    }
635 	    if (Extend1 && r1 != 0)
636 		return R_tensor_cone_apex(pfs, rect, x0, y0, r0, x1, y1, r1, t1);
637 	    return 0;
638 	} else {
639 	    if (Extend1) {
640 		r = R_rect_radius(rect, x1, y1);
641 		code = R_obtuse_cone(pfs, rect, x1, y1, r1, x0, y0, r0, t1, r);
642 		if (code < 0)
643 		    return code;
644 	    }
645 	    if (Extend0 && r0 != 0)
646 		return R_tensor_cone_apex(pfs, rect, x1, y1, r1, x0, y0, r0, t0);
647 	}
648     } else {
649 	/* Acute cone or cylinder. */
650 	double x2, y2, r2, x3, y3, r3;
651 
652 	if (Extend0) {
653 	    R_outer_circle(pfs, rect, x1, y1, r1, x0, y0, r0, &x3, &y3, &r3);
654 	    if (x3 != x1 || y3 != y1) {
655 		code = R_tensor_annulus(pfs, rect, x0, y0, r0, t0, x3, y3, r3, t0);
656 		if (code < 0)
657 		    return code;
658 	    }
659 	}
660 	if (Extend1) {
661 	    R_outer_circle(pfs, rect, x0, y0, r0, x1, y1, r1, &x2, &y2, &r2);
662 	    if (x2 != x0 || y2 != y0) {
663 		code = R_tensor_annulus(pfs, rect, x1, y1, r1, t1, x2, y2, r2, t1);
664 		if (code < 0)
665 		    return code;
666 	    }
667 	}
668     }
669     return 0;
670 }
671 
672 private 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)673 gs_shading_R_fill_rectangle_aux(const gs_shading_t * psh0, const gs_rect * rect,
674 			    const gs_fixed_rect *clip_rect,
675 			    gx_device * dev, gs_imager_state * pis)
676 {
677     const gs_shading_R_t *const psh = (const gs_shading_R_t *)psh0;
678     R_fill_state_t state;
679     float d0 = psh->params.Domain[0], d1 = psh->params.Domain[1];
680     float dd = d1 - d0;
681     float x0 = psh->params.Coords[0], y0 = psh->params.Coords[1];
682     floatp r0 = psh->params.Coords[2];
683     float x1 = psh->params.Coords[3], y1 = psh->params.Coords[4];
684     floatp r1 = psh->params.Coords[5];
685     int code;
686     float dist_between_circles;
687     gs_point dev_dpt;
688     gs_point dev_dr;
689     patch_fill_state_t pfs1;
690 
691     shade_init_fill_state((shading_fill_state_t *)&state, psh0, dev, pis);
692     state.psh = psh;
693     state.rect = *rect;
694     /* Compute the parameter range. */
695     code = gs_function_evaluate(psh->params.Function, &d0,
696 			     state.frame.cc[0].paint.values);
697     if (code < 0)
698 	return code;
699     code = gs_function_evaluate(psh->params.Function, &d1,
700 			     state.frame.cc[1].paint.values);
701     if (code < 0)
702 	return code;
703     state.delta.x = x1 - x0;
704     state.delta.y = y1 - y0;
705     state.dr = r1 - r0;
706 
707     gs_distance_transform(state.delta.x, state.delta.y, &ctm_only(pis), &dev_dpt);
708     gs_distance_transform(state.dr, 0, &ctm_only(pis), &dev_dr);
709 
710     dist_between_circles = hypot(x1-x0, y1-y0);
711 
712     state.dd = dd;
713     memcpy(&pfs1, (shading_fill_state_t *)&state , sizeof(shading_fill_state_t));
714     pfs1.Function = psh->params.Function;
715     code = init_patch_fill_state(&pfs1);
716     if (code < 0)
717 	return code;
718     pfs1.rect = *clip_rect;
719     pfs1.maybe_self_intersecting = false;
720     code = R_extensions(&pfs1, psh, rect, d0, d1, psh->params.Extend[0], false);
721     if (code < 0)
722 	return code;
723     {
724 	float x0 = psh->params.Coords[0], y0 = psh->params.Coords[1];
725 	floatp r0 = psh->params.Coords[2];
726 	float x1 = psh->params.Coords[3], y1 = psh->params.Coords[4];
727 	floatp r1 = psh->params.Coords[5];
728 
729 	code = R_tensor_annulus(&pfs1, rect, x0, y0, r0, d0, x1, y1, r1, d1);
730 	if (code < 0)
731 	    return code;
732     }
733     return R_extensions(&pfs1, psh, rect, d0, d1, false, psh->params.Extend[1]);
734 }
735 
736 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)737 gs_shading_R_fill_rectangle(const gs_shading_t * psh0, const gs_rect * rect,
738 			    const gs_fixed_rect * rect_clip,
739 			    gx_device * dev, gs_imager_state * pis)
740 {
741     int code;
742 
743     if (VD_TRACE_RADIAL_PATCH && vd_allowed('s')) {
744 	vd_get_dc('s');
745 	vd_set_shift(0, 0);
746 	vd_set_scale(0.01);
747 	vd_set_origin(0, 0);
748     }
749     code = gs_shading_R_fill_rectangle_aux(psh0, rect, rect_clip, dev, pis);
750     if (VD_TRACE_FUNCTIONAL_PATCH && vd_allowed('s'))
751 	vd_release_dc;
752     return code;
753 }
754