Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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 
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
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 
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
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 
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
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
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
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
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
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
}