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