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 |
}
|