/src/ghostpdl/base/gxfill.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* A topological spot decomposition algorithm with dropout prevention. */ |
18 | | /* |
19 | | This is a dramaticly reorganized and improved revision of the |
20 | | filling algorithm, which was initially coded by Henry Stiles. |
21 | | The main improvements are: |
22 | | 1. A dropout prevention for character fill. |
23 | | 2. The spot topology analysis support |
24 | | for True Type grid fitting. |
25 | | 3. Fixed the contiguity of a spot covering |
26 | | for shading fills with no dropouts. |
27 | | */ |
28 | | /* See is_spotan about the spot topology analysis support. */ |
29 | | /* Also defining lower-level path filling procedures */ |
30 | | |
31 | | #include "gx.h" |
32 | | #include "gserrors.h" |
33 | | #include "gsstruct.h" |
34 | | #include "gxfixed.h" |
35 | | #include "gxdevice.h" |
36 | | #include "gzpath.h" |
37 | | #include "gzcpath.h" |
38 | | #include "gxdcolor.h" |
39 | | #include "gxhttile.h" |
40 | | #include "gxgstate.h" |
41 | | #include "gxpaint.h" /* for prototypes */ |
42 | | #include "gxfill.h" |
43 | | #include "gxpath.h" |
44 | | #include "gsptype1.h" |
45 | | #include "gsptype2.h" |
46 | | #include "gxpcolor.h" |
47 | | #include "gdevddrw.h" |
48 | | #include "gzspotan.h" /* Only for gx_san_trap_store. */ |
49 | | #include "memory_.h" |
50 | | #include "stdint_.h" |
51 | | #include "gsstate.h" /* for gs_currentcpsimode */ |
52 | | #include "gxdevsop.h" |
53 | | #include "gxscanc.h" |
54 | | /* |
55 | | #include "gxfilltr.h" - Do not remove this comment. "gxfilltr.h" is included below. |
56 | | #include "gxfillsl.h" - Do not remove this comment. "gxfillsl.h" is included below. |
57 | | #include "gxfillts.h" - Do not remove this comment. "gxfillts.h" is included below. |
58 | | */ |
59 | | #include "assert_.h" |
60 | | |
61 | | /* #define ENABLE_TRAP_AMALGAMATION */ |
62 | | |
63 | | /* |
64 | | * ENABLE_TRAP_AMALGAMATION - Experimental trap amalgamation code, disabled |
65 | | * by default. |
66 | | * |
67 | | * Set this if you think that the potential gains from only having to send |
68 | | * one trapezoid rather than 3 trapezoids justifies the costs in |
69 | | * calculating whether this is possible. |
70 | | * |
71 | | * Note: Due to rounding inaccuracies while scan converting, there are |
72 | | * cases where the rectangles can round to different pixel boundaries than |
73 | | * the trapezoids do. This means that enabling the TRY_TO_EXTEND_TRAP |
74 | | * define will cause rendering differences, but tests seem to indicate that |
75 | | * this only happens in very borderline cases. |
76 | | */ |
77 | | #ifdef ENABLE_TRAP_AMALGAMATION |
78 | | #define TRY_TO_EXTEND_TRAP 1 |
79 | | #else |
80 | 0 | #define TRY_TO_EXTEND_TRAP 0 |
81 | | #endif |
82 | | |
83 | | #ifdef COLLECT_STATS_FILL |
84 | | /* Define the statistics structure instance. */ |
85 | | stats_fill_t stats_fill; |
86 | | #endif |
87 | | |
88 | | /* A predicate for spot insideness. */ |
89 | | /* rule = -1 for winding number rule, i.e. */ |
90 | | /* we are inside if the winding number is non-zero; */ |
91 | | /* rule = 1 for even-odd rule, i.e. */ |
92 | | /* we are inside if the winding number is odd. */ |
93 | 0 | #define INSIDE_PATH_P(inside, rule) ((inside & rule) != 0) |
94 | | |
95 | | /* ---------------- Active line management ---------------- */ |
96 | | |
97 | | /* |
98 | | * Define the ordering criterion for active lines that overlap in Y. |
99 | | * Return -1, 0, or 1 if lp1 precedes, coincides with, or follows lp2. |
100 | | * |
101 | | * The lines' x_current values are correct for some Y value that crosses |
102 | | * both of them and that is not both the start of one and the end of the |
103 | | * other. (Neither line is horizontal.) We want the ordering at this |
104 | | * Y value, or, of the x_current values are equal, greater Y values |
105 | | * (if any: this Y value might be the end of both lines). |
106 | | */ |
107 | | static int |
108 | | x_order(const active_line *lp1, const active_line *lp2) |
109 | 0 | { |
110 | 0 | bool s1; |
111 | |
|
112 | 0 | INCR(order); |
113 | 0 | if (!lp1 || !lp2 || lp1->x_current < lp2->x_current) |
114 | 0 | return -1; |
115 | 0 | else if (lp1->x_current > lp2->x_current) |
116 | 0 | return 1; |
117 | | /* |
118 | | * We need to compare the slopes of the lines. Start by |
119 | | * checking one fast case, where the slopes have opposite signs. |
120 | | */ |
121 | 0 | if ((s1 = lp1->start.x < lp1->end.x) != (lp2->start.x < lp2->end.x)) |
122 | 0 | return (s1 ? 1 : -1); |
123 | | /* |
124 | | * We really do have to compare the slopes. Fortunately, this isn't |
125 | | * needed very often. We want the sign of the comparison |
126 | | * dx1/dy1 - dx2/dy2, or (since we know dy1 and dy2 are positive) |
127 | | * dx1 * dy2 - dx2 * dy1. In principle, we can't simply do this using |
128 | | * doubles, since we need complete accuracy and doubles don't have |
129 | | * enough fraction bits. However, with the usual 20+12-bit fixeds and |
130 | | * 64-bit doubles, both of the factors would have to exceed 2^15 |
131 | | * device space pixels for the result to be inaccurate, so we |
132 | | * simply disregard this possibility. ****** FIX SOMEDAY ****** |
133 | | */ |
134 | 0 | INCR(slow_order); |
135 | 0 | { |
136 | 0 | fixed dx1 = lp1->end.x - lp1->start.x, |
137 | 0 | dy1 = lp1->end.y - lp1->start.y; |
138 | 0 | fixed dx2 = lp2->end.x - lp2->start.x, |
139 | 0 | dy2 = lp2->end.y - lp2->start.y; |
140 | 0 | double diff = (double)dx1 * dy2 - (double)dx2 * dy1; |
141 | |
|
142 | 0 | return (diff < 0 ? -1 : diff > 0 ? 1 : 0); |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | /* |
147 | | * The active_line structure isn't really simple, but since its instances |
148 | | * only exist temporarily during a fill operation, we don't have to |
149 | | * worry about a garbage collection occurring. |
150 | | */ |
151 | | gs_private_st_simple(st_active_line, active_line, "active_line"); |
152 | | |
153 | | #ifdef DEBUG |
154 | | /* Internal procedures for printing and checking active lines. */ |
155 | | static void |
156 | | print_active_line(const gs_memory_t *mem, const char *label, const active_line * alp) |
157 | | { |
158 | | dmlprintf5(mem, "[f]%s "PRI_INTPTR"(%d): x_current=%f x_next=%f\n", |
159 | | label, (intptr_t)alp, alp->direction, |
160 | | fixed2float(alp->x_current), fixed2float(alp->x_next)); |
161 | | dmlprintf5(mem, " start=(%f,%f) pt_end="PRI_INTPTR"(%f,%f)\n", |
162 | | fixed2float(alp->start.x), fixed2float(alp->start.y), |
163 | | (intptr_t)alp->pseg, |
164 | | fixed2float(alp->end.x), fixed2float(alp->end.y)); |
165 | | dmlprintf2(mem, " prev="PRI_INTPTR" next="PRI_INTPTR"\n", |
166 | | (intptr_t)alp->prev, (intptr_t)alp->next); |
167 | | } |
168 | | static void |
169 | | print_line_list(const gs_memory_t *mem, const active_line * flp) |
170 | | { |
171 | | const active_line *lp; |
172 | | |
173 | | for (lp = flp; lp != 0; lp = lp->next) { |
174 | | fixed xc = lp->x_current, xn = lp->x_next; |
175 | | |
176 | | dmlprintf3(mem, "[f]"PRI_INTPTR"(%d): x_current/next=%g", |
177 | | (intptr_t)lp, lp->direction, |
178 | | fixed2float(xc)); |
179 | | if (xn != xc) |
180 | | dmprintf1(mem, "/%g", fixed2float(xn)); |
181 | | dmputc(mem, '\n'); |
182 | | } |
183 | | } |
184 | | static inline void |
185 | | print_al(const gs_memory_t *mem, const char *label, const active_line * alp) |
186 | | { |
187 | | if (gs_debug_c('F')) |
188 | | print_active_line(mem, label, alp); |
189 | | } |
190 | | #else |
191 | 0 | #define print_al(mem,label,alp) DO_NOTHING |
192 | | #endif |
193 | | |
194 | | static inline bool |
195 | | is_spotan_device(gx_device * dev) |
196 | 20.7M | { |
197 | | /* Use open_device procedure to identify the type of the device |
198 | | * instead of the standard gs_object_type() because gs_cpath_accum_device |
199 | | * is allocated on the stack i.e. has no block header with a descriptor |
200 | | * but has dev->memory set like a heap-allocated device. |
201 | | */ |
202 | 20.7M | return dev_proc(dev, open_device) == san_open; |
203 | 20.7M | } |
204 | | |
205 | | /* Forward declarations */ |
206 | | static void free_line_list(line_list *); |
207 | | static int add_y_list(gx_path *, line_list *); |
208 | | static int add_y_line_aux(const segment * prev_lp, const segment * lp, |
209 | | const gs_fixed_point *curr, const gs_fixed_point *prev, int dir, line_list *ll); |
210 | | static void insert_x_new(active_line *, line_list *); |
211 | | static int end_x_line(active_line *, const line_list *, bool); |
212 | | static int step_al(active_line *alp, bool move_iterator); |
213 | | |
214 | 0 | #define FILL_LOOP_PROC(proc) int proc(line_list *, fixed band_mask) |
215 | | static FILL_LOOP_PROC(spot_into_scan_lines); |
216 | | static FILL_LOOP_PROC(spot_into_trapezoids); |
217 | | |
218 | | /* |
219 | | * This is the general path filling algorithm. |
220 | | * It uses the center-of-pixel rule for filling |
221 | | * We can implement Microsoft's upper-left-corner-of-pixel rule |
222 | | * by subtracting (0.5, 0.5) from all the coordinates in the path. |
223 | | * |
224 | | * The adjust parameters are a hack for keeping regions |
225 | | * from coming out too faint: they specify an amount by which to expand |
226 | | * the sides of every filled region. |
227 | | * Setting adjust = fixed_half is supposed to produce the effect of Adobe's |
228 | | * any-part-of-pixel rule, but it doesn't quite, because of the |
229 | | * closed/open interval rule for regions. We detect this as a special case |
230 | | * and do the slightly ugly things necessary to make it work. |
231 | | */ |
232 | | |
233 | | /* Initialize the line list for a path. */ |
234 | | static inline void |
235 | | init_line_list(line_list *ll, gs_memory_t * mem) |
236 | 0 | { |
237 | 0 | ll->memory = mem; |
238 | 0 | ll->active_area = 0; |
239 | 0 | ll->next_active = ll->local_active; |
240 | 0 | ll->limit = ll->next_active + MAX_LOCAL_ACTIVE; |
241 | 0 | ll->close_count = 0; |
242 | 0 | ll->y_list = 0; |
243 | 0 | ll->y_line = 0; |
244 | 0 | ll->h_list0 = ll->h_list1 = 0; |
245 | |
|
246 | 0 | ll->x_head.prev = NULL; |
247 | | /* Bug 695234: Initialise the following to pacify valgrind */ |
248 | 0 | ll->x_head.start.x = 0; |
249 | 0 | ll->x_head.start.y = 0; |
250 | 0 | ll->x_head.end.x = 0; |
251 | 0 | ll->x_head.end.y = 0; |
252 | | |
253 | | /* Do not initialize ll->bbox_left, ll->bbox_width - they were set in advance. */ |
254 | 0 | INCR(fill); |
255 | 0 | } |
256 | | |
257 | | /* Unlink any line_close segments added temporarily. */ |
258 | | static inline void |
259 | | unclose_path(gx_path * ppath, int count) |
260 | 0 | { |
261 | 0 | subpath *psub; |
262 | |
|
263 | 0 | for (psub = ppath->first_subpath; count != 0; |
264 | 0 | psub = (subpath *) psub->last->next |
265 | 0 | ) |
266 | 0 | if (psub->last == (segment *) & psub->closer) { |
267 | 0 | segment *prev = psub->closer.prev, *next = psub->closer.next; |
268 | |
|
269 | 0 | prev->next = next; |
270 | 0 | if (next) |
271 | 0 | next->prev = prev; |
272 | 0 | psub->last = prev; |
273 | 0 | count--; |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | | /* |
278 | | * The general fill path algorithm. |
279 | | */ |
280 | | static int |
281 | | gx_general_fill_path(gx_device * pdev, const gs_gstate * pgs, |
282 | | gx_path * ppath, const gx_fill_params * params, |
283 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
284 | 113M | { |
285 | 113M | gs_fixed_point adjust; |
286 | 113M | gs_logical_operation_t lop = pgs->log_op; |
287 | 113M | gs_fixed_rect ibox, bbox, sbox; |
288 | 113M | gx_device_clip cdev; |
289 | 113M | gx_device *dev = pdev; |
290 | 113M | gx_device *save_dev = dev; |
291 | 113M | gx_path ffpath; |
292 | 113M | gx_path *pfpath; |
293 | 113M | int code; |
294 | 113M | int max_fill_band = dev->max_fill_band; |
295 | 113M | #define NO_BAND_MASK ((fixed)(-1) << (sizeof(fixed) * 8 - 1)) |
296 | 113M | const bool is_character = params->adjust.x == -1; /* See gxgstate.h */ |
297 | 113M | bool fill_by_trapezoids; |
298 | 113M | bool big_path = ppath->subpath_count > 50; |
299 | 113M | fill_options fo; |
300 | 113M | line_list lst; |
301 | 113M | int clipping = 0; |
302 | 113M | int scanconverter; |
303 | | |
304 | 113M | *(const fill_options **)&lst.fo = &fo; /* break 'const'. */ |
305 | | /* |
306 | | * Compute the bounding box before we flatten the path. |
307 | | * This can save a lot of time if the path has curves. |
308 | | * If the path is neither fully within nor fully outside |
309 | | * the quick-check boxes, we could recompute the bounding box |
310 | | * and make the checks again after flattening the path, |
311 | | * but right now we don't bother. |
312 | | */ |
313 | 113M | gx_path_bbox(ppath, &ibox); |
314 | 113M | if (is_character) |
315 | 0 | adjust.x = adjust.y = 0; |
316 | 113M | else |
317 | 113M | adjust = params->adjust; |
318 | 113M | lst.contour_count = 0; |
319 | 113M | lst.windings = NULL; |
320 | 113M | lst.bbox_left = fixed2int(ibox.p.x - adjust.x - fixed_epsilon); |
321 | 113M | lst.bbox_width = fixed2int(fixed_ceiling(ibox.q.x + adjust.x)) - lst.bbox_left; |
322 | | /* Check the bounding boxes. */ |
323 | 113M | if_debug6m('f', pdev->memory, "[f]adjust=%g,%g bbox=(%g,%g),(%g,%g)\n", |
324 | 113M | fixed2float(adjust.x), fixed2float(adjust.y), |
325 | 113M | fixed2float(ibox.p.x), fixed2float(ibox.p.y), |
326 | 113M | fixed2float(ibox.q.x), fixed2float(ibox.q.y)); |
327 | 113M | if (pcpath) |
328 | 22.0M | gx_cpath_inner_box(pcpath, &bbox); |
329 | 91.0M | else |
330 | 91.0M | (*dev_proc(dev, get_clipping_box)) (dev, &bbox); |
331 | 113M | if (!rect_within(ibox, bbox)) { |
332 | | /* |
333 | | * Intersect the path box and the clip bounding box. |
334 | | * If the intersection is empty, this fill is a no-op. |
335 | | */ |
336 | 102M | if (pcpath) |
337 | 18.2M | gx_cpath_outer_box(pcpath, &bbox); |
338 | 102M | if_debug4m('f', pdev->memory, " outer_box=(%g,%g),(%g,%g)\n", |
339 | 102M | fixed2float(bbox.p.x), fixed2float(bbox.p.y), |
340 | 102M | fixed2float(bbox.q.x), fixed2float(bbox.q.y)); |
341 | 102M | rect_intersect(ibox, bbox); |
342 | 102M | if (ibox.p.x - adjust.x >= ibox.q.x + adjust.x || |
343 | 102M | ibox.p.y - adjust.y >= ibox.q.y + adjust.y |
344 | 102M | ) { /* Intersection of boxes is empty! */ |
345 | 92.2M | return 0; |
346 | 92.2M | } |
347 | | /* |
348 | | * The path is neither entirely inside the inner clip box |
349 | | * nor entirely outside the outer clip box. |
350 | | * If we had to flatten the path, this is where we would |
351 | | * recompute its bbox and make the tests again, |
352 | | * but we don't bother right now. |
353 | | * |
354 | | * If there is a clipping path, set up a clipping device. |
355 | | */ |
356 | 10.2M | if (pcpath) { |
357 | 3.15M | dev = (gx_device *) & cdev; |
358 | 3.15M | gx_make_clip_device_on_stack(&cdev, pcpath, save_dev); |
359 | 3.15M | cdev.max_fill_band = save_dev->max_fill_band; |
360 | 3.15M | clipping = 1; |
361 | 3.15M | } |
362 | 10.2M | } |
363 | | /* |
364 | | * Compute the proper adjustment values. |
365 | | * To get the effect of the any-part-of-pixel rule, |
366 | | * we may have to tweak them slightly. |
367 | | * NOTE: We changed the adjust_right/above value from 0.5+epsilon |
368 | | * to 0.5 in release 5.01; even though this does the right thing |
369 | | * in every case we could imagine, we aren't confident that it's |
370 | | * correct. (The old values were definitely incorrect, since they |
371 | | * caused 1-pixel-wide/high objects to color 2 pixels even if |
372 | | * they fell exactly on pixel boundaries.) |
373 | | */ |
374 | 20.7M | if (adjust.x == fixed_half) |
375 | 18.7M | fo.adjust_left = fixed_half - fixed_epsilon, |
376 | 18.7M | fo.adjust_right = fixed_half /* + fixed_epsilon */ ; /* see above */ |
377 | 1.97M | else |
378 | 1.97M | fo.adjust_left = fo.adjust_right = adjust.x; |
379 | 20.7M | if (adjust.y == fixed_half) |
380 | 18.7M | fo.adjust_below = fixed_half - fixed_epsilon, |
381 | 18.7M | fo.adjust_above = fixed_half /* + fixed_epsilon */ ; /* see above */ |
382 | 1.97M | else |
383 | 1.97M | fo.adjust_below = fo.adjust_above = adjust.y; |
384 | 20.7M | sbox.p.x = ibox.p.x - adjust.x; |
385 | 20.7M | sbox.p.y = ibox.p.y - adjust.y; |
386 | 20.7M | sbox.q.x = ibox.q.x + adjust.x; |
387 | 20.7M | sbox.q.y = ibox.q.y + adjust.y; |
388 | 20.7M | fo.pdevc = pdevc; |
389 | 20.7M | fo.lop = lop; |
390 | 20.7M | fo.fixed_flat = float2fixed(params->flatness); |
391 | 20.7M | fo.ymin = ibox.p.y; |
392 | 20.7M | fo.ymax = ibox.q.y; |
393 | 20.7M | fo.dev = dev; |
394 | 20.7M | fo.pbox = &sbox; |
395 | 20.7M | fo.rule = params->rule; |
396 | 20.7M | fo.is_spotan = is_spotan_device(dev); |
397 | 20.7M | fo.fill_direct = color_writes_pure(pdevc, lop); |
398 | 20.7M | fo.fill_rect = (fo.fill_direct ? dev_proc(dev, fill_rectangle) : NULL); |
399 | 20.7M | fo.fill_trap = dev_proc(dev, fill_trapezoid); |
400 | | |
401 | | /* |
402 | | * We have a choice of two different filling algorithms: |
403 | | * scan-line-based and trapezoid-based. They compare as follows: |
404 | | * |
405 | | * Scan Trap |
406 | | * ---- ---- |
407 | | * skip +draw 0-height horizontal lines |
408 | | * slow +fast rectangles |
409 | | * +fast slow curves |
410 | | * +yes no write pixels at most once when adjust != 0 |
411 | | * |
412 | | * Normally we use the scan line algorithm for characters, where curve |
413 | | * speed is important, and for non-idempotent RasterOps, where double |
414 | | * pixel writing must be avoided, and the trapezoid algorithm otherwise. |
415 | | * However, we always use the trapezoid algorithm for rectangles. |
416 | | */ |
417 | 20.7M | fill_by_trapezoids = (!gx_path_has_curves(ppath) || |
418 | 20.7M | params->flatness >= 1.0 || fo.is_spotan); |
419 | | |
420 | 20.7M | if (fill_by_trapezoids && !fo.is_spotan && !lop_is_idempotent(lop)) { |
421 | 673k | gs_fixed_rect rbox; |
422 | | |
423 | 673k | if (gx_path_is_rectangular(ppath, &rbox)) { |
424 | 494k | int x0 = fixed2int_pixround(rbox.p.x - fo.adjust_left); |
425 | 494k | int y0 = fixed2int_pixround(rbox.p.y - fo.adjust_below); |
426 | 494k | int x1 = fixed2int_pixround(rbox.q.x + fo.adjust_right); |
427 | 494k | int y1 = fixed2int_pixround(rbox.q.y + fo.adjust_above); |
428 | | |
429 | 494k | code = gx_fill_rectangle_device_rop(x0, y0, x1 - x0, y1 - y0, |
430 | 494k | pdevc, dev, lop); |
431 | 494k | goto exit; |
432 | 494k | } |
433 | 178k | if (fo.adjust_left | fo.adjust_right | fo.adjust_below | fo.adjust_above) |
434 | 144k | fill_by_trapezoids = false; /* avoid double writing pixels */ |
435 | 178k | } |
436 | | |
437 | 20.2M | if (!fo.is_spotan && ((scanconverter = gs_getscanconverter(pdev->memory)) >= GS_SCANCONVERTER_EDGEBUFFER || |
438 | 20.2M | (scanconverter == GS_SCANCONVERTER_DEFAULT && GS_SCANCONVERTER_DEFAULT_IS_EDGEBUFFER))) { |
439 | 20.2M | gx_scan_converter_t *sc; |
440 | | /* If we have a request for accurate curves, make sure we exactly |
441 | | * match what we'd get for stroking. */ |
442 | 20.2M | if (!big_path && pgs->accurate_curves && gx_path_has_curves(ppath)) |
443 | 7.06M | { |
444 | 7.06M | gx_path_init_local(&ffpath, ppath->memory); |
445 | 7.06M | code = gx_path_copy_reducing(ppath, &ffpath, fo.fixed_flat, NULL, |
446 | 7.06M | pco_small_curves | pco_accurate); |
447 | 7.06M | if (code < 0) |
448 | 0 | goto exit; |
449 | 7.06M | ppath = &ffpath; |
450 | 7.06M | } |
451 | | |
452 | 20.2M | if (fill_by_trapezoids && !lop_is_idempotent(lop)) |
453 | 33.7k | fill_by_trapezoids = 0; |
454 | 20.2M | if (!fill_by_trapezoids) |
455 | 2.05M | { |
456 | 2.05M | if (adjust.x == 0 && adjust.y == 0) |
457 | 1.22M | sc = &gx_scan_converter; |
458 | 830k | else |
459 | 830k | sc = &gx_scan_converter_app; |
460 | 18.2M | } else { |
461 | 18.2M | if (adjust.x == 0 && adjust.y == 0) |
462 | 751k | sc = &gx_scan_converter_tr; |
463 | 17.4M | else |
464 | 17.4M | sc = &gx_scan_converter_tr_app; |
465 | 18.2M | } |
466 | 20.2M | code = gx_scan_convert_and_fill(sc, |
467 | 20.2M | dev, |
468 | 20.2M | ppath, |
469 | 20.2M | &ibox, |
470 | 20.2M | fo.fixed_flat, |
471 | 20.2M | params->rule, |
472 | 20.2M | pdevc, |
473 | 20.2M | (!fill_by_trapezoids && fo.fill_direct) ? -1 : (int)pgs->log_op); |
474 | 20.2M | if (ppath == &ffpath) |
475 | 7.06M | gx_path_free(ppath, "gx_general_fill_path"); |
476 | 20.2M | goto exit; |
477 | 20.2M | } |
478 | | |
479 | 0 | gx_path_init_local(&ffpath, ppath->memory); |
480 | 0 | if (!big_path && !gx_path_has_curves(ppath)) /* don't need to flatten */ |
481 | 0 | pfpath = ppath; |
482 | 0 | else if (is_spotan_device(dev)) |
483 | 0 | pfpath = ppath; |
484 | 0 | else if (!big_path && !pgs->accurate_curves && gx_path__check_curves(ppath, pco_small_curves, fo.fixed_flat)) |
485 | 0 | pfpath = ppath; |
486 | 0 | else { |
487 | 0 | code = gx_path_copy_reducing(ppath, &ffpath, fo.fixed_flat, NULL, |
488 | 0 | pco_small_curves | (pgs->accurate_curves ? pco_accurate : 0)); |
489 | 0 | if (code < 0) |
490 | 0 | goto exit; |
491 | 0 | pfpath = &ffpath; |
492 | 0 | if (big_path) { |
493 | 0 | code = gx_path_merge_contacting_contours(pfpath); |
494 | 0 | if (code < 0) |
495 | 0 | goto exit; |
496 | 0 | } |
497 | 0 | } |
498 | 0 | fo.fill_by_trapezoids = fill_by_trapezoids; |
499 | | /* Initialize the active line list. */ |
500 | 0 | init_line_list(&lst, ppath->memory); |
501 | 0 | if ((code = add_y_list(pfpath, &lst)) < 0) |
502 | 0 | goto nope; |
503 | 0 | { |
504 | 0 | FILL_LOOP_PROC((*fill_loop)); |
505 | | |
506 | | /* Some short-sighted compilers won't allow a conditional here.... */ |
507 | 0 | if (fill_by_trapezoids) |
508 | 0 | fill_loop = spot_into_trapezoids; |
509 | 0 | else |
510 | 0 | fill_loop = spot_into_scan_lines; |
511 | 0 | if (gs_currentcpsimode(pgs->memory) && is_character) { |
512 | 0 | if (lst.contour_count > countof(lst.local_windings)) { |
513 | 0 | lst.windings = (int *)gs_alloc_byte_array(pdev->memory, lst.contour_count, |
514 | 0 | sizeof(int), "gx_general_fill_path"); |
515 | 0 | if (lst.windings == NULL) |
516 | 0 | return_error(gs_error_VMerror); |
517 | 0 | } else |
518 | 0 | lst.windings = lst.local_windings; |
519 | 0 | memset(lst.windings, 0, sizeof(lst.windings[0]) * lst.contour_count); |
520 | 0 | } |
521 | 0 | code = (*fill_loop) |
522 | 0 | (&lst, (max_fill_band == 0 ? NO_BAND_MASK : int2fixed(-max_fill_band))); |
523 | 0 | if (lst.windings != NULL && lst.windings != lst.local_windings) |
524 | 0 | gs_free_object(pdev->memory, lst.windings, "gx_general_fill_path"); |
525 | 0 | } |
526 | 0 | nope:if (lst.close_count != 0) |
527 | 0 | unclose_path(pfpath, lst.close_count); |
528 | 0 | free_line_list(&lst); |
529 | 0 | if (pfpath != ppath) /* had to flatten */ |
530 | 0 | gx_path_free(pfpath, "gx_general_fill_path"); |
531 | | #ifdef COLLECT_STATS_FILL |
532 | | if (gs_debug_c('f')) { |
533 | | dmlputs(ppath->memory, |
534 | | "[f] # alloc up down horiz step slowx iter find band bstep bfill\n"); |
535 | | dmlprintf5(ppath->memory, " %5ld %5ld %5ld %5ld %5ld", |
536 | | stats_fill.fill, stats_fill.fill_alloc, |
537 | | stats_fill.y_up, stats_fill.y_down, |
538 | | stats_fill.horiz); |
539 | | dmlprintf4(ppath->memory, " %5ld %5ld %5ld %5ld", |
540 | | stats_fill.x_step, stats_fill.slow_x, |
541 | | stats_fill.iter, stats_fill.find_y); |
542 | | dmlprintf3(ppath->memory, " %5ld %5ld %5ld\n", |
543 | | stats_fill.band, stats_fill.band_step, |
544 | | stats_fill.band_fill); |
545 | | dmlputs(ppath->memory, |
546 | | "[f] afill slant shall sfill mqcrs order slowo\n"); |
547 | | dmlprintf7(ppath->memory, " %5ld %5ld %5ld %5ld %5ld %5ld %5ld\n", |
548 | | stats_fill.afill, stats_fill.slant, |
549 | | stats_fill.slant_shallow, stats_fill.sfill, |
550 | | stats_fill.mq_cross, stats_fill.order, |
551 | | stats_fill.slow_order); |
552 | | } |
553 | | #endif |
554 | 20.7M | exit: |
555 | 20.7M | if (clipping) |
556 | 3.15M | gx_destroy_clip_device_on_stack(&cdev); |
557 | 20.7M | return code; |
558 | 0 | } |
559 | | |
560 | | static int |
561 | | pass_shading_area_through_clip_path_device(gx_device * pdev, const gs_gstate * pgs, |
562 | | gx_path * ppath, const gx_fill_params * params, |
563 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
564 | 0 | { |
565 | 0 | if (pdevc == NULL) { |
566 | 0 | gx_device_clip *cdev = (gx_device_clip *)pdev; |
567 | |
|
568 | 0 | return dev_proc(cdev->target, fill_path)(cdev->target, pgs, ppath, params, pdevc, pcpath); |
569 | 0 | } |
570 | | /* We know that tha clip path device implements fill_path with default proc. */ |
571 | 0 | return gx_default_fill_path(pdev, pgs, ppath, params, pdevc, pcpath); |
572 | 0 | } |
573 | | |
574 | | /* Optimization for shading and halftone fill : |
575 | | The general filling algorithm subdivides the fill region into |
576 | | trapezoid or rectangle subregions and then paints each subregion |
577 | | with given color. If the color is complex, it also needs to be subdivided |
578 | | into constant color rectangles. In the worst case it gives |
579 | | a multiple of numbers of rectangles, which may be too slow. |
580 | | A faster processing may be obtained with installing a clipper |
581 | | device with the filling path, and then render the complex color |
582 | | through it. The speeding up happens due to the clipper device |
583 | | is optimised for fast scans through neighbour clipping rectangles. |
584 | | */ |
585 | | int |
586 | | gx_default_fill_path_shading_or_pattern(gx_device * pdev, const gs_gstate * pgs, |
587 | | gx_path * ppath, const gx_fill_params * params, |
588 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
589 | 779k | { |
590 | 779k | int code = 0, clipping = 0; |
591 | | /* We need a single clipping path here, because shadings and |
592 | | halftones don't take 2 paths. Compute the clipping path intersection. |
593 | | */ |
594 | 779k | gx_clip_path cpath_intersection, cpath_with_shading_bbox; |
595 | 779k | const gx_clip_path *pcpath1, *pcpath2; |
596 | 779k | gs_gstate *pgs_noconst = (gs_gstate *)pgs; /* Break const. */ |
597 | | |
598 | 779k | if (ppath != NULL) { |
599 | 720k | code = gx_cpath_init_local_shared_nested(&cpath_intersection, pcpath, pdev->memory, 1); |
600 | 720k | if (code < 0) |
601 | 0 | return code; |
602 | 720k | if (pcpath == NULL) { |
603 | 292k | gs_fixed_rect clip_box1; |
604 | | |
605 | 292k | (*dev_proc(pdev, get_clipping_box)) (pdev, &clip_box1); |
606 | 292k | code = gx_cpath_from_rectangle(&cpath_intersection, &clip_box1); |
607 | 292k | } |
608 | 720k | if (code >= 0) |
609 | 720k | code = gx_cpath_intersect_with_params(&cpath_intersection, ppath, params->rule, |
610 | 720k | pgs_noconst, params); |
611 | 720k | pcpath1 = &cpath_intersection; |
612 | 720k | } else |
613 | 59.3k | pcpath1 = pcpath; |
614 | 779k | pcpath2 = pcpath1; |
615 | 779k | if (code >= 0) |
616 | 779k | code = gx_dc_pattern2_clip_with_bbox(pdevc, pdev, &cpath_with_shading_bbox, &pcpath1); |
617 | | /* Do fill : */ |
618 | 779k | if (code >= 0) { |
619 | 779k | gs_fixed_rect clip_box; |
620 | 779k | gs_int_rect cb; |
621 | 779k | const gx_rop_source_t *rs = NULL; |
622 | 779k | gx_device *dev; |
623 | 779k | gx_device_clip cdev; |
624 | | |
625 | 779k | gx_cpath_outer_box(pcpath1, &clip_box); |
626 | 779k | cb.p.x = fixed2int_pixround(clip_box.p.x); |
627 | 779k | cb.p.y = fixed2int_pixround(clip_box.p.y); |
628 | 779k | cb.q.x = fixed2int_pixround(clip_box.q.x); |
629 | 779k | cb.q.y = fixed2int_pixround(clip_box.q.y); |
630 | 779k | if (gx_dc_is_pattern2_color(pdevc) && |
631 | 779k | (*dev_proc(pdev, dev_spec_op))(pdev, |
632 | 62.0k | gxdso_pattern_handles_clip_path, NULL, 0) > 0) { |
633 | | /* A special interaction with clist writer device : |
634 | | pass the intersected clipping path. It uses an unusual call to |
635 | | fill_path with NULL device color. */ |
636 | 2.03k | code = (*dev_proc(pdev, fill_path))(pdev, pgs, ppath, params, NULL, pcpath1); |
637 | 2.03k | dev = pdev; |
638 | 777k | } else { |
639 | 777k | gx_make_clip_device_on_stack(&cdev, pcpath1, pdev); |
640 | 777k | dev = (gx_device *)&cdev; |
641 | 777k | if ((*dev_proc(pdev, dev_spec_op))(pdev, |
642 | 777k | gxdso_pattern_shading_area, NULL, 0) > 0) |
643 | 0 | set_dev_proc(&cdev, fill_path, pass_shading_area_through_clip_path_device); |
644 | 777k | code = 0; |
645 | 777k | clipping = 1; |
646 | 777k | } |
647 | 779k | if (code >= 0) { |
648 | | /* Check clip rectangle covers an actual area */ |
649 | 779k | if (cb.p.x != cb.q.x && cb.p.y != cb.q.y) |
650 | 403k | code = pdevc->type->fill_rectangle(pdevc, |
651 | 403k | cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y, |
652 | 403k | dev, pgs->log_op, rs); |
653 | 779k | } |
654 | 779k | if (clipping) |
655 | 777k | gx_destroy_clip_device_on_stack(&cdev); |
656 | 779k | } |
657 | 779k | if (ppath != NULL) |
658 | 720k | gx_cpath_free(&cpath_intersection, "shading_fill_cpath_intersection"); |
659 | 779k | if (pcpath1 != pcpath2) |
660 | 93 | gx_cpath_free(&cpath_with_shading_bbox, "shading_fill_cpath_intersection"); |
661 | | |
662 | 779k | return code; |
663 | 779k | } |
664 | | |
665 | | /* |
666 | | * Fill a path. This is the default implementation of the driver |
667 | | * fill_path procedure. |
668 | | */ |
669 | | int |
670 | | gx_default_fill_path(gx_device * pdev, const gs_gstate * pgs, |
671 | | gx_path * ppath, const gx_fill_params * params, |
672 | | const gx_device_color * pdevc, const gx_clip_path * pcpath) |
673 | 113M | { |
674 | 113M | if (gx_dc_is_pattern2_color(pdevc) || |
675 | 113M | pdevc->type == &gx_dc_type_data_ht_colored || |
676 | 113M | (gx_dc_is_pattern1_color(pdevc) && |
677 | 113M | gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile))) |
678 | 721k | return gx_default_fill_path_shading_or_pattern(pdev, pgs, ppath, params, pdevc, pcpath); |
679 | 113M | else |
680 | 113M | return gx_general_fill_path(pdev, pgs, ppath, params, pdevc, pcpath); |
681 | 113M | } |
682 | | |
683 | | int |
684 | | gx_default_lock_pattern(gx_device *pdev, |
685 | | gs_gstate *pgs, |
686 | | gs_id pattern_id, |
687 | | int lock) |
688 | 328 | { |
689 | 328 | return gx_pattern_cache_entry_set_lock(pgs, pattern_id, lock); |
690 | 328 | } |
691 | | |
692 | | /* |
693 | | * Fill/Stroke a path. This is the default implementation of the driver |
694 | | * fill_path procedure. |
695 | | */ |
696 | | int |
697 | | gx_default_fill_stroke_path(gx_device * pdev, const gs_gstate * pgs, |
698 | | gx_path * ppath, |
699 | | const gx_fill_params * params_fill, |
700 | | const gx_device_color * pdevc_fill, |
701 | | const gx_stroke_params * params_stroke, |
702 | | const gx_device_color * pdevc_stroke, |
703 | | const gx_clip_path * pcpath) |
704 | 57.8k | { |
705 | 57.8k | int code = dev_proc(pdev, fill_path)(pdev, pgs, ppath, params_fill, pdevc_fill, pcpath); |
706 | | |
707 | 57.8k | if (code < 0) |
708 | 0 | return code; |
709 | | /* Swap colors to make sure the pgs colorspace is correct for stroke */ |
710 | 57.8k | gs_swapcolors_quick(pgs); |
711 | 57.8k | code = dev_proc(pdev, stroke_path)(pdev, pgs, ppath, params_stroke, pdevc_stroke, pcpath); |
712 | 57.8k | gs_swapcolors_quick(pgs); |
713 | 57.8k | return code; |
714 | 57.8k | } |
715 | | |
716 | | /* Free the line list. */ |
717 | | static void |
718 | | free_line_list(line_list *ll) |
719 | 0 | { |
720 | | /* Free any individually allocated active_lines. */ |
721 | 0 | gs_memory_t *mem = ll->memory; |
722 | 0 | active_line *alp; |
723 | |
|
724 | 0 | while ((alp = ll->active_area) != 0) { |
725 | 0 | active_line *next = alp->alloc_next; |
726 | |
|
727 | 0 | gs_free_object(mem, alp, "active line"); |
728 | 0 | ll->active_area = next; |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | | static inline active_line * |
733 | | make_al(line_list *ll) |
734 | 0 | { |
735 | 0 | active_line *alp = ll->next_active; |
736 | |
|
737 | 0 | if (alp == ll->limit) { /* Allocate separately */ |
738 | 0 | alp = gs_alloc_struct(ll->memory, active_line, |
739 | 0 | &st_active_line, "active line"); |
740 | 0 | if (alp == 0) |
741 | 0 | return NULL; |
742 | 0 | alp->alloc_next = ll->active_area; |
743 | 0 | ll->active_area = alp; |
744 | 0 | INCR(fill_alloc); |
745 | 0 | } else |
746 | 0 | ll->next_active++; |
747 | 0 | alp->contour_count = ll->contour_count; |
748 | 0 | return alp; |
749 | 0 | } |
750 | | |
751 | | /* Insert the new line in the Y ordering */ |
752 | | static void |
753 | | insert_y_line(line_list *ll, active_line *alp) |
754 | 0 | { |
755 | 0 | active_line *yp = ll->y_line; |
756 | 0 | active_line *nyp; |
757 | 0 | fixed y_start = alp->start.y; |
758 | |
|
759 | 0 | if (yp == 0) { |
760 | 0 | alp->next = alp->prev = 0; |
761 | 0 | ll->y_list = alp; |
762 | 0 | } else if (y_start >= yp->start.y) { /* Insert the new line after y_line */ |
763 | 0 | while (INCR_EXPR(y_up), |
764 | 0 | ((nyp = yp->next) != NULL && |
765 | 0 | y_start > nyp->start.y) |
766 | 0 | ) |
767 | 0 | yp = nyp; |
768 | 0 | alp->next = nyp; |
769 | 0 | alp->prev = yp; |
770 | 0 | yp->next = alp; |
771 | 0 | if (nyp) |
772 | 0 | nyp->prev = alp; |
773 | 0 | } else { /* Insert the new line before y_line */ |
774 | 0 | while (INCR_EXPR(y_down), |
775 | 0 | ((nyp = yp->prev) != NULL && |
776 | 0 | y_start < nyp->start.y) |
777 | 0 | ) |
778 | 0 | yp = nyp; |
779 | 0 | alp->prev = nyp; |
780 | 0 | alp->next = yp; |
781 | 0 | yp->prev = alp; |
782 | 0 | if (nyp) |
783 | 0 | nyp->next = alp; |
784 | 0 | else |
785 | 0 | ll->y_list = alp; |
786 | 0 | } |
787 | 0 | ll->y_line = alp; |
788 | 0 | print_al(ll->memory, "add ", alp); |
789 | 0 | } |
790 | | |
791 | | typedef struct contour_cursor_s { |
792 | | segment *prev, *pseg, *pfirst, *plast; |
793 | | gx_flattened_iterator fi; |
794 | | bool more_flattened; |
795 | | bool first_flattened; |
796 | | int dir; |
797 | | bool monotonic_y; |
798 | | bool monotonic_x; |
799 | | bool crossing; |
800 | | } contour_cursor; |
801 | | |
802 | | static inline int |
803 | | compute_dir(const fill_options *fo, fixed y0, fixed y1) |
804 | 0 | { |
805 | 0 | if (max(y0, y1) < fo->ymin) |
806 | 0 | return DIR_OUT_OF_Y_RANGE; |
807 | 0 | if (min(y0, y1) > fo->ymax) |
808 | 0 | return DIR_OUT_OF_Y_RANGE; |
809 | 0 | return (y0 < y1 ? DIR_UP : |
810 | 0 | y0 > y1 ? DIR_DOWN : DIR_HORIZONTAL); |
811 | 0 | } |
812 | | |
813 | | static inline int |
814 | | add_y_curve_part(line_list *ll, segment *s0, segment *s1, int dir, |
815 | | gx_flattened_iterator *fi, bool more1, bool step_back, bool monotonic_x) |
816 | 0 | { |
817 | 0 | active_line *alp = make_al(ll); |
818 | 0 | int code; |
819 | |
|
820 | 0 | if (alp == NULL) |
821 | 0 | return_error(gs_error_VMerror); |
822 | 0 | alp->pseg = (dir == DIR_UP ? s1 : s0); |
823 | 0 | alp->direction = dir; |
824 | 0 | alp->fi = *fi; |
825 | 0 | alp->more_flattened = more1; |
826 | 0 | if (dir != DIR_UP && more1) |
827 | 0 | gx_flattened_iterator__switch_to_backscan(&alp->fi, more1); |
828 | 0 | if (step_back) { |
829 | 0 | do { |
830 | 0 | code = gx_flattened_iterator__prev(&alp->fi); |
831 | 0 | if (code < 0) |
832 | 0 | return code; |
833 | 0 | alp->more_flattened = code; |
834 | 0 | if (compute_dir(ll->fo, alp->fi.ly0, alp->fi.ly1) != 2) |
835 | 0 | break; |
836 | 0 | } while (alp->more_flattened); |
837 | 0 | } |
838 | 0 | code = step_al(alp, false); |
839 | 0 | if (code < 0) |
840 | 0 | return code; |
841 | 0 | alp->monotonic_y = false; |
842 | 0 | alp->monotonic_x = monotonic_x; |
843 | 0 | insert_y_line(ll, alp); |
844 | 0 | return 0; |
845 | 0 | } |
846 | | |
847 | | static inline int |
848 | | add_y_line(const segment * prev_lp, const segment * lp, int dir, line_list *ll) |
849 | 0 | { |
850 | 0 | return add_y_line_aux(prev_lp, lp, &lp->pt, &prev_lp->pt, dir, ll); |
851 | 0 | } |
852 | | |
853 | | static inline int |
854 | | start_al_pair(line_list *ll, contour_cursor *q, contour_cursor *p) |
855 | 0 | { |
856 | 0 | int code; |
857 | |
|
858 | 0 | if (q->monotonic_y) |
859 | 0 | code = add_y_line(q->prev, q->pseg, DIR_DOWN, ll); |
860 | 0 | else |
861 | 0 | code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi, |
862 | 0 | !q->first_flattened, false, q->monotonic_x); |
863 | 0 | if (code < 0) |
864 | 0 | return code; |
865 | 0 | if (p->monotonic_y) |
866 | 0 | code = add_y_line(p->prev, p->pseg, DIR_UP, ll); |
867 | 0 | else |
868 | 0 | code = add_y_curve_part(ll, p->prev, p->pseg, DIR_UP, &p->fi, |
869 | 0 | p->more_flattened, false, p->monotonic_x); |
870 | 0 | return code; |
871 | 0 | } |
872 | | |
873 | | /* Start active lines from a minimum of a possibly non-monotonic curve. */ |
874 | | static int |
875 | | start_al_pair_from_min(line_list *ll, contour_cursor *q) |
876 | 0 | { |
877 | 0 | int dir, code; |
878 | 0 | const fill_options * const fo = ll->fo; |
879 | | |
880 | | /* q stands at the first segment, which isn't last. */ |
881 | 0 | do { |
882 | 0 | code = gx_flattened_iterator__next(&q->fi); |
883 | 0 | if (code < 0) |
884 | 0 | return code; |
885 | 0 | q->more_flattened = code; |
886 | 0 | dir = compute_dir(fo, q->fi.ly0, q->fi.ly1); |
887 | 0 | if (q->fi.ly0 > fo->ymax && ll->y_break > q->fi.y0) |
888 | 0 | ll->y_break = q->fi.ly0; |
889 | 0 | if (q->fi.ly1 > fo->ymax && ll->y_break > q->fi.ly1) |
890 | 0 | ll->y_break = q->fi.ly1; |
891 | 0 | if (q->fi.ly0 >= fo->ymin) { |
892 | 0 | if (dir == DIR_UP && ll->main_dir == DIR_DOWN) { |
893 | 0 | code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi, |
894 | 0 | true, true, q->monotonic_x); |
895 | 0 | if (code < 0) |
896 | 0 | return code; |
897 | 0 | code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, &q->fi, |
898 | 0 | q->more_flattened, false, q->monotonic_x); |
899 | 0 | if (code < 0) |
900 | 0 | return code; |
901 | 0 | } else if (q->fi.ly1 < fo->ymin) { |
902 | 0 | code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi, |
903 | 0 | true, false, q->monotonic_x); |
904 | 0 | if (code < 0) |
905 | 0 | return code; |
906 | 0 | } |
907 | 0 | } else if (q->fi.ly1 >= fo->ymin) { |
908 | 0 | code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, &q->fi, |
909 | 0 | q->more_flattened, false, q->monotonic_x); |
910 | 0 | if (code < 0) |
911 | 0 | return code; |
912 | 0 | } |
913 | 0 | q->first_flattened = false; |
914 | 0 | q->dir = dir; |
915 | 0 | if (dir == DIR_DOWN || dir == DIR_UP) |
916 | 0 | ll->main_dir = dir; |
917 | 0 | } while(q->more_flattened); |
918 | | /* q stands at the last segment. */ |
919 | 0 | return 0; |
920 | | /* note : it doesn't depend on the number of curve minimums, |
921 | | which may vary due to arithmetic errors. */ |
922 | 0 | } |
923 | | |
924 | | static inline int |
925 | | init_contour_cursor(const fill_options * const fo, contour_cursor *q) |
926 | 0 | { |
927 | 0 | if (q->pseg->type == s_curve) { |
928 | 0 | curve_segment *s = (curve_segment *)q->pseg; |
929 | 0 | fixed ymin = min(min(q->prev->pt.y, s->p1.y), min(s->p2.y, s->pt.y)); |
930 | 0 | fixed ymax = max(max(q->prev->pt.y, s->p1.y), max(s->p2.y, s->pt.y)); |
931 | 0 | bool in_band = ymin <= fo->ymax && ymax >= fo->ymin; |
932 | 0 | q->crossing = ymin < fo->ymin && ymax >= fo->ymin; |
933 | 0 | q->monotonic_y = !in_band || |
934 | 0 | (!q->crossing && |
935 | 0 | ((q->prev->pt.y <= s->p1.y && s->p1.y <= s->p2.y && s->p2.y <= s->pt.y) || |
936 | 0 | (q->prev->pt.y >= s->p1.y && s->p1.y >= s->p2.y && s->p2.y >= s->pt.y))); |
937 | 0 | q->monotonic_x = |
938 | 0 | ((q->prev->pt.x <= s->p1.x && s->p1.x <= s->p2.x && s->p2.x <= s->pt.x) || |
939 | 0 | (q->prev->pt.x >= s->p1.x && s->p1.x >= s->p2.x && s->p2.x >= s->pt.x)); |
940 | 0 | } else |
941 | 0 | q->monotonic_y = true; |
942 | 0 | if (!q->monotonic_y) { |
943 | 0 | curve_segment *s = (curve_segment *)q->pseg; |
944 | 0 | int k = gx_curve_log2_samples(q->prev->pt.x, q->prev->pt.y, s, fo->fixed_flat); |
945 | |
|
946 | 0 | if (!gx_flattened_iterator__init(&q->fi, q->prev->pt.x, q->prev->pt.y, s, k)) |
947 | 0 | return_error(gs_error_rangecheck); |
948 | 0 | } else { |
949 | 0 | q->dir = compute_dir(fo, q->prev->pt.y, q->pseg->pt.y); |
950 | 0 | gx_flattened_iterator__init_line(&q->fi, |
951 | 0 | q->prev->pt.x, q->prev->pt.y, q->pseg->pt.x, q->pseg->pt.y); /* fake for curves. */ |
952 | 0 | } |
953 | 0 | q->first_flattened = true; |
954 | 0 | return 0; |
955 | 0 | } |
956 | | |
957 | | static int |
958 | | scan_contour(line_list *ll, segment *pfirst, segment *plast, segment *prev) |
959 | 0 | { |
960 | 0 | contour_cursor p, q; |
961 | 0 | int code; |
962 | 0 | bool only_horizontal = true, saved = false; |
963 | 0 | const fill_options * const fo = ll->fo; |
964 | 0 | contour_cursor save_q; |
965 | |
|
966 | 0 | q.prev = prev; |
967 | 0 | q.pseg = plast; |
968 | 0 | q.pfirst = pfirst; |
969 | 0 | q.plast = plast; |
970 | |
|
971 | 0 | memset(&save_q, 0, sizeof(save_q)); /* Quiet gcc warning. */ |
972 | 0 | p.monotonic_x = false; /* Quiet gcc warning. */ |
973 | 0 | q.monotonic_x = false; /* Quiet gcc warning. */ |
974 | | |
975 | | /* The first thing we do is to walk BACKWARDS along the contour (aka the subpath or |
976 | | * set of curve segments). We stop either when we reach the beginning again, |
977 | | * or when we hit a non-horizontal segment. If we hit the beginning, then we |
978 | | * remember that the segment is 'only_horizontal'. Otherwise, we remember the |
979 | | * direction of this contour (UP or DOWN). */ |
980 | 0 | save_q.dir = DIR_OUT_OF_Y_RANGE; |
981 | 0 | ll->main_dir = DIR_HORIZONTAL; |
982 | 0 | for (; ; q.pseg = q.prev, q.prev = q.prev->prev) { |
983 | | /* Prepare to walk FORWARDS along the single curve segment in 'q'. */ |
984 | 0 | code = init_contour_cursor(fo, &q); |
985 | 0 | if (code < 0) |
986 | 0 | return code; |
987 | 0 | for (;;) { |
988 | 0 | code = gx_flattened_iterator__next(&q.fi); |
989 | 0 | assert(code >= 0); /* Indicates an internal error */ |
990 | 0 | if (code < 0) |
991 | 0 | return code; |
992 | 0 | q.dir = compute_dir(fo, q.fi.ly0, q.fi.ly1); |
993 | 0 | if (q.dir == DIR_DOWN || q.dir == DIR_UP) |
994 | 0 | ll->main_dir = q.dir; |
995 | | /* Exit the loop when we hit the end of the single curve segment */ |
996 | 0 | if (!code) |
997 | 0 | break; |
998 | 0 | q.first_flattened = false; |
999 | 0 | } |
1000 | | /* Thus first_flattened == true, iff the curve needed no subdivisions */ |
1001 | 0 | q.more_flattened = false; |
1002 | | /* ll->maindir == DIR_HORIZONTAL, iff the curve is entirely horizontal |
1003 | | * (or entirely out of y range). It will contain whatever the last UP or DOWN |
1004 | | * section was otherwise. */ |
1005 | 0 | if (ll->main_dir != DIR_HORIZONTAL) { |
1006 | 0 | only_horizontal = false; |
1007 | | /* Now we know we're not entirely horizontal, we can abort this run. */ |
1008 | 0 | break; |
1009 | 0 | } |
1010 | | /* If we haven't saved one yet, and we are not out of range, save this one. |
1011 | | * i.e. save_q and save_fi are the first 'in range' section. We can save |
1012 | | * time in subsequent passes through by starting here. */ |
1013 | 0 | if (!saved && q.dir != DIR_OUT_OF_Y_RANGE) { |
1014 | 0 | save_q = q; |
1015 | 0 | saved = true; |
1016 | 0 | } |
1017 | 0 | if (q.prev == q.pfirst) |
1018 | 0 | break; /* Exit if we're back at the start of the contour */ |
1019 | 0 | } |
1020 | | |
1021 | | /* Second run through; this is where we actually do the hard work. */ |
1022 | | /* Restore q if we saved it above */ |
1023 | 0 | if (saved) { |
1024 | 0 | q = save_q; |
1025 | 0 | } |
1026 | | /* q is now at the start of a run that we know will head in an |
1027 | | * ll->main_dir direction. Start p at the next place and walk |
1028 | | * forwards. */ |
1029 | 0 | for (p.prev = q.pfirst; p.prev != q.plast; p.prev = p.pseg) { |
1030 | 0 | bool added; |
1031 | 0 | p.pseg = p.prev->next; |
1032 | | /* Can we ignore this segment entirely? */ |
1033 | 0 | if (!only_horizontal && p.pseg->type != s_curve && |
1034 | 0 | p.prev->pt.x == p.pseg->pt.x && p.prev->pt.y == p.pseg->pt.y) |
1035 | 0 | continue; |
1036 | | |
1037 | | /* Prepare to walk down 'p' */ |
1038 | 0 | code = init_contour_cursor(fo, &p); |
1039 | 0 | if (code < 0) |
1040 | 0 | return code; |
1041 | | |
1042 | 0 | do { |
1043 | | /* Find the next flattened section that is within range */ |
1044 | 0 | do { |
1045 | 0 | code = gx_flattened_iterator__next(&p.fi); |
1046 | 0 | assert(code >= 0); /* Indicates an internal error */ |
1047 | 0 | if (code < 0) |
1048 | 0 | return code; |
1049 | 0 | p.more_flattened = code; |
1050 | 0 | p.dir = compute_dir(fo, p.fi.ly0, p.fi.ly1); |
1051 | 0 | } while (p.more_flattened && p.dir == DIR_OUT_OF_Y_RANGE); |
1052 | | |
1053 | | /* So either we're in range, or we've reached the end of the segment (or both) */ |
1054 | | /* FIXME: Can we break here if p.dir == DIR_OUT_OF_Y_RANGE? */ |
1055 | | |
1056 | | /* Set ll->y_break to be the smallest line endpoint we find > ymax */ |
1057 | 0 | if (p.fi.ly0 > fo->ymax && ll->y_break > p.fi.ly0) |
1058 | 0 | ll->y_break = p.fi.ly0; |
1059 | 0 | if (p.fi.ly1 > fo->ymax && ll->y_break > p.fi.ly1) |
1060 | 0 | ll->y_break = p.fi.ly1; |
1061 | |
|
1062 | 0 | added = 0; |
1063 | 0 | if (p.dir == DIR_HORIZONTAL) { |
1064 | 0 | if (p.monotonic_y) { |
1065 | 0 | if ( |
1066 | | #ifdef FILL_ZERO_WIDTH |
1067 | | (fo->adjust_below | fo->adjust_above) != 0) { |
1068 | | #else |
1069 | 0 | (fo->adjust_below + fo->adjust_above >= (fixed_1 - fixed_epsilon) || |
1070 | 0 | fixed2int_pixround(p.pseg->pt.y - fo->adjust_below) < |
1071 | 0 | fixed2int_pixround(p.pseg->pt.y + fo->adjust_above))) { |
1072 | 0 | #endif |
1073 | | /* Add it here to avoid double processing in process_h_segments. */ |
1074 | 0 | code = add_y_line(p.prev, p.pseg, DIR_HORIZONTAL, ll); |
1075 | 0 | if (code < 0) |
1076 | 0 | return code; |
1077 | 0 | added = 1; |
1078 | 0 | } |
1079 | 0 | } |
1080 | 0 | } else { |
1081 | 0 | if (p.fi.ly0 >= fo->ymin) |
1082 | 0 | { |
1083 | 0 | if (p.dir == DIR_UP && ll->main_dir == DIR_DOWN) { |
1084 | | /* p starts within range, and heads up. q was heading down. Therefore |
1085 | | * they meet at a local minima. Start a pair of active lines from that. */ |
1086 | 0 | code = start_al_pair(ll, &q, &p); |
1087 | 0 | if (code < 0) |
1088 | 0 | return code; |
1089 | 0 | added = 1; |
1090 | 0 | } else if (p.fi.ly1 < fo->ymin) { |
1091 | | /* p heading downwards */ |
1092 | 0 | if (p.monotonic_y) |
1093 | 0 | code = add_y_line(p.prev, p.pseg, DIR_DOWN, ll); |
1094 | 0 | else |
1095 | 0 | code = add_y_curve_part(ll, p.prev, p.pseg, DIR_DOWN, &p.fi, |
1096 | 0 | !p.first_flattened, false, p.monotonic_x); |
1097 | 0 | if (code < 0) |
1098 | 0 | return code; |
1099 | 0 | added = 1; |
1100 | 0 | } |
1101 | 0 | } else if (p.fi.ly1 >= fo->ymin) { |
1102 | | /* p heading upwards */ |
1103 | 0 | if (p.monotonic_y) |
1104 | 0 | code = add_y_line(p.prev, p.pseg, DIR_UP, ll); |
1105 | 0 | else |
1106 | 0 | code = add_y_curve_part(ll, p.prev, p.pseg, DIR_UP, &p.fi, |
1107 | 0 | p.more_flattened, false, p.monotonic_x); |
1108 | 0 | if (code < 0) |
1109 | 0 | return code; |
1110 | 0 | added = 1; |
1111 | 0 | } |
1112 | 0 | if (p.dir == DIR_DOWN || p.dir == DIR_UP) |
1113 | 0 | ll->main_dir = p.dir; |
1114 | 0 | } |
1115 | 0 | if (!p.monotonic_y && p.more_flattened) { |
1116 | 0 | code = start_al_pair_from_min(ll, &p); |
1117 | 0 | if (code < 0) |
1118 | 0 | return code; |
1119 | 0 | added = 1; |
1120 | 0 | } |
1121 | 0 | if (p.dir == DIR_DOWN || p.dir == DIR_HORIZONTAL) { |
1122 | 0 | q.prev = p.prev; |
1123 | 0 | q.pseg = p.pseg; |
1124 | 0 | q.monotonic_y = p.monotonic_y; |
1125 | 0 | q.more_flattened = p.more_flattened; |
1126 | 0 | q.first_flattened = p.first_flattened; |
1127 | 0 | q.fi = p.fi; |
1128 | 0 | q.dir = p.dir; |
1129 | 0 | } |
1130 | | /* If we haven't added lines based on this segment, and there |
1131 | | * are more flattened lines to come from it, ensure we loop |
1132 | | * to get the rest of them. */ |
1133 | | /* FIXME: Do we need '!added' here? Try removing it. */ |
1134 | 0 | } while (!added && p.more_flattened); |
1135 | 0 | } |
1136 | 0 | return 0; |
1137 | 0 | } |
1138 | | |
1139 | | /* |
1140 | | * Construct a Y-sorted list of segments for rasterizing a path. We assume |
1141 | | * the path is non-empty. Only include non-horizontal lines or (monotonic) |
1142 | | * curve segments where one endpoint is locally Y-minimal, and horizontal |
1143 | | * lines that might color some additional pixels. |
1144 | | */ |
1145 | | static int |
1146 | | add_y_list(gx_path * ppath, line_list *ll) |
1147 | 0 | { |
1148 | 0 | subpath *psub = ppath->first_subpath; |
1149 | 0 | int close_count = 0; |
1150 | 0 | int code; |
1151 | |
|
1152 | 0 | ll->y_break = max_fixed; |
1153 | |
|
1154 | 0 | for (;psub; psub = (subpath *)psub->last->next) { |
1155 | | /* We know that pseg points to a subpath head (s_start). */ |
1156 | 0 | segment *pfirst = (segment *)psub; |
1157 | 0 | segment *plast = psub->last, *prev; |
1158 | |
|
1159 | 0 | if (plast->type != s_line_close) { |
1160 | | /* Create a fake s_line_close */ |
1161 | 0 | line_close_segment *lp = &psub->closer; |
1162 | 0 | segment *next = plast->next; |
1163 | |
|
1164 | 0 | lp->next = next; |
1165 | 0 | lp->prev = plast; |
1166 | 0 | plast->next = (segment *) lp; |
1167 | 0 | if (next) |
1168 | 0 | next->prev = (segment *) lp; |
1169 | 0 | lp->type = s_line_close; |
1170 | 0 | lp->pt = psub->pt; |
1171 | 0 | lp->sub = psub; |
1172 | 0 | psub->last = plast = (segment *) lp; |
1173 | 0 | ll->close_count++; |
1174 | 0 | } |
1175 | 0 | prev = plast->prev; |
1176 | 0 | code = scan_contour(ll, pfirst, plast, prev); |
1177 | 0 | if (code < 0) |
1178 | 0 | return code; |
1179 | 0 | ll->contour_count++; |
1180 | 0 | } |
1181 | 0 | return close_count; |
1182 | 0 | } |
1183 | | |
1184 | | static int |
1185 | | step_al(active_line *alp, bool move_iterator) |
1186 | 0 | { |
1187 | 0 | bool forth = (alp->direction == DIR_UP || !alp->fi.curve); |
1188 | |
|
1189 | 0 | if (move_iterator) { |
1190 | 0 | int code; |
1191 | |
|
1192 | 0 | if (forth) |
1193 | 0 | code = gx_flattened_iterator__next(&alp->fi); |
1194 | 0 | else |
1195 | 0 | code = gx_flattened_iterator__prev(&alp->fi); |
1196 | 0 | if (code < 0) |
1197 | 0 | return code; |
1198 | 0 | alp->more_flattened = code; |
1199 | 0 | } |
1200 | | /* Note that we can get alp->fi.ly0 == alp->fi.ly1 |
1201 | | if the curve tangent is horizontal. */ |
1202 | 0 | alp->start.x = (forth ? alp->fi.lx0 : alp->fi.lx1); |
1203 | 0 | alp->start.y = (forth ? alp->fi.ly0 : alp->fi.ly1); |
1204 | 0 | alp->end.x = (forth ? alp->fi.lx1 : alp->fi.lx0); |
1205 | 0 | alp->end.y = (forth ? alp->fi.ly1 : alp->fi.ly0); |
1206 | 0 | alp->diff.x = alp->end.x - alp->start.x; |
1207 | 0 | alp->diff.y = alp->end.y - alp->start.y; |
1208 | 0 | SET_NUM_ADJUST(alp); |
1209 | 0 | (alp)->y_fast_max = MAX_MINUS_NUM_ADJUST(alp) / |
1210 | 0 | ((alp->diff.x >= 0 ? alp->diff.x : -alp->diff.x) | 1) + alp->start.y; |
1211 | 0 | return 0; |
1212 | 0 | } |
1213 | | |
1214 | | static int |
1215 | | init_al(active_line *alp, const segment *s0, const segment *s1, const line_list *ll) |
1216 | 0 | { |
1217 | 0 | const segment *ss = (alp->direction == DIR_UP ? s1 : s0); |
1218 | | /* Warning : p0 may be equal to &alp->end. */ |
1219 | 0 | bool curve = (ss != NULL && ss->type == s_curve); |
1220 | 0 | int code; |
1221 | |
|
1222 | 0 | if (curve) { |
1223 | 0 | if (alp->direction == DIR_UP) { |
1224 | 0 | const curve_segment *cs = (const curve_segment *)s1; |
1225 | 0 | int k = gx_curve_log2_samples(s0->pt.x, s0->pt.y, cs, ll->fo->fixed_flat); |
1226 | |
|
1227 | 0 | gx_flattened_iterator__init(&alp->fi, |
1228 | 0 | s0->pt.x, s0->pt.y, (const curve_segment *)s1, k); |
1229 | 0 | code = step_al(alp, true); |
1230 | 0 | if (code < 0) |
1231 | 0 | return code; |
1232 | 0 | if (!ll->fo->fill_by_trapezoids) { |
1233 | 0 | alp->monotonic_y = (s0->pt.y <= cs->p1.y && cs->p1.y <= cs->p2.y && cs->p2.y <= cs->pt.y); |
1234 | 0 | alp->monotonic_x = (s0->pt.x <= cs->p1.x && cs->p1.x <= cs->p2.x && cs->p2.x <= cs->pt.x) || |
1235 | 0 | (s0->pt.x >= cs->p1.x && cs->p1.x >= cs->p2.x && cs->p2.x >= cs->pt.x); |
1236 | 0 | } |
1237 | 0 | } else { |
1238 | 0 | const curve_segment *cs = (const curve_segment *)s0; |
1239 | 0 | int k = gx_curve_log2_samples(s1->pt.x, s1->pt.y, cs, ll->fo->fixed_flat); |
1240 | 0 | bool more; |
1241 | |
|
1242 | 0 | gx_flattened_iterator__init(&alp->fi, |
1243 | 0 | s1->pt.x, s1->pt.y, (const curve_segment *)s0, k); |
1244 | 0 | alp->more_flattened = false; |
1245 | 0 | do { |
1246 | 0 | code = gx_flattened_iterator__next(&alp->fi); |
1247 | 0 | if (code < 0) |
1248 | 0 | return code; |
1249 | 0 | more = code; |
1250 | 0 | alp->more_flattened |= more; |
1251 | 0 | } while(more); |
1252 | 0 | gx_flattened_iterator__switch_to_backscan(&alp->fi, alp->more_flattened); |
1253 | 0 | code = step_al(alp, false); |
1254 | 0 | if (code < 0) |
1255 | 0 | return code; |
1256 | 0 | if (!ll->fo->fill_by_trapezoids) { |
1257 | 0 | alp->monotonic_y = (s0->pt.y >= cs->p1.y && cs->p1.y >= cs->p2.y && cs->p2.y >= cs->pt.y); |
1258 | 0 | alp->monotonic_x = (s0->pt.x <= cs->p1.x && cs->p1.x <= cs->p2.x && cs->p2.x <= cs->pt.x) || |
1259 | 0 | (s0->pt.x >= cs->p1.x && cs->p1.x >= cs->p2.x && cs->p2.x >= cs->pt.x); |
1260 | 0 | } |
1261 | 0 | } |
1262 | 0 | } else { |
1263 | 0 | gx_flattened_iterator__init_line(&alp->fi, |
1264 | 0 | s0->pt.x, s0->pt.y, s1->pt.x, s1->pt.y); |
1265 | 0 | code = step_al(alp, true); |
1266 | 0 | if (code < 0) |
1267 | 0 | return code; |
1268 | 0 | alp->monotonic_x = alp->monotonic_y = true; |
1269 | 0 | } |
1270 | 0 | alp->pseg = s1; |
1271 | 0 | return 0; |
1272 | 0 | } |
1273 | | /* |
1274 | | * Internal routine to test a segment and add it to the pending list if |
1275 | | * appropriate. |
1276 | | */ |
1277 | | static int |
1278 | | add_y_line_aux(const segment * prev_lp, const segment * lp, |
1279 | | const gs_fixed_point *curr, const gs_fixed_point *prev, int dir, line_list *ll) |
1280 | 0 | { |
1281 | 0 | int code; |
1282 | |
|
1283 | 0 | active_line *alp = make_al(ll); |
1284 | 0 | if (alp == NULL) |
1285 | 0 | return_error(gs_error_VMerror); |
1286 | 0 | alp->more_flattened = false; |
1287 | 0 | switch ((alp->direction = dir)) { |
1288 | 0 | case DIR_UP: |
1289 | 0 | code = init_al(alp, prev_lp, lp, ll); |
1290 | 0 | if (code < 0) |
1291 | 0 | return code; |
1292 | 0 | break; |
1293 | 0 | case DIR_DOWN: |
1294 | 0 | code = init_al(alp, lp, prev_lp, ll); |
1295 | 0 | if (code < 0) |
1296 | 0 | return code; |
1297 | 0 | break; |
1298 | 0 | case DIR_HORIZONTAL: |
1299 | 0 | alp->start = *prev; |
1300 | 0 | alp->end = *curr; |
1301 | | /* Don't need to set dx or y_fast_max */ |
1302 | 0 | alp->pseg = prev_lp; /* may not need this either */ |
1303 | 0 | break; |
1304 | 0 | default: /* can't happen */ |
1305 | 0 | return_error(gs_error_unregistered); |
1306 | 0 | } |
1307 | 0 | insert_y_line(ll, alp); |
1308 | 0 | return 0; |
1309 | 0 | } |
1310 | | |
1311 | | /* ---------------- Filling loop utilities ---------------- */ |
1312 | | |
1313 | | /* Insert a newly active line in the X ordering. */ |
1314 | | static void |
1315 | | insert_x_new(active_line * alp, line_list *ll) |
1316 | 0 | { |
1317 | 0 | active_line *next; |
1318 | 0 | active_line *prev = &ll->x_head; |
1319 | |
|
1320 | 0 | alp->x_current = alp->start.x; |
1321 | 0 | alp->x_next = alp->start.x; /* If the spot starts with a horizontal segment, |
1322 | | we need resort_x_line to work properly |
1323 | | in the very beginning. */ |
1324 | 0 | while (INCR_EXPR(x_step), |
1325 | 0 | (next = prev->next) != 0 && x_order(next, alp) < 0 |
1326 | 0 | ) |
1327 | 0 | prev = next; |
1328 | 0 | alp->next = next; |
1329 | 0 | alp->prev = prev; |
1330 | 0 | if (next != 0) |
1331 | 0 | next->prev = alp; |
1332 | 0 | prev->next = alp; |
1333 | 0 | } |
1334 | | |
1335 | | /* Insert a newly active line in the h list. */ |
1336 | | /* h list isn't ordered because x intervals may overlap. */ |
1337 | | /* todo : optimize with maintaining ordered interval list; |
1338 | | Unite contacting inrevals, like we did in add_margin. |
1339 | | */ |
1340 | | static inline void |
1341 | | insert_h_new(active_line * alp, line_list *ll) |
1342 | 0 | { |
1343 | 0 | alp->next = ll->h_list0; |
1344 | 0 | alp->prev = 0; |
1345 | 0 | if (ll->h_list0 != 0) |
1346 | 0 | ll->h_list0->prev = alp; |
1347 | 0 | ll->h_list0 = alp; |
1348 | | /* |
1349 | | * h list keeps horizontal lines for a given y. |
1350 | | * There are 2 lists, corresponding to upper and lower ends |
1351 | | * of the Y-interval, which spot_into_trapezoids currently proceeds. |
1352 | | * Parts of horizontal lines, which do not contact a filled trapezoid, |
1353 | | * are parts of the spot bondairy. They to be marked in |
1354 | | * the 'sect' array. - see process_h_lists. |
1355 | | */ |
1356 | 0 | } |
1357 | | |
1358 | | static inline void |
1359 | | remove_al(const line_list *ll, active_line *alp) |
1360 | 0 | { |
1361 | 0 | active_line *nlp = alp->next; |
1362 | |
|
1363 | 0 | alp->prev->next = nlp; |
1364 | 0 | if (nlp) |
1365 | 0 | nlp->prev = alp->prev; |
1366 | 0 | if_debug1m('F', ll->memory, "[F]drop "PRI_INTPTR"\n", (intptr_t)alp); |
1367 | 0 | } |
1368 | | |
1369 | | /* |
1370 | | * Handle a line segment that just ended. Return true iff this was |
1371 | | * the end of a line sequence. |
1372 | | */ |
1373 | | static int |
1374 | | end_x_line(active_line *alp, const line_list *ll, bool update) |
1375 | 0 | { |
1376 | 0 | const segment *pseg = alp->pseg; |
1377 | | /* |
1378 | | * The computation of next relies on the fact that |
1379 | | * all subpaths have been closed. When we cycle |
1380 | | * around to the other end of a subpath, we must be |
1381 | | * sure not to process the start/end point twice. |
1382 | | */ |
1383 | 0 | const segment *next = |
1384 | 0 | (alp->direction == DIR_UP ? |
1385 | 0 | ( /* Upward line, go forward along path. */ |
1386 | 0 | pseg->type == s_line_close ? /* end of subpath */ |
1387 | 0 | ((const line_close_segment *)pseg)->sub->next : |
1388 | 0 | pseg->next) : |
1389 | 0 | ( /* Downward line, go backward along path. */ |
1390 | 0 | pseg->type == s_start ? /* start of subpath */ |
1391 | 0 | ((const subpath *)pseg)->last->prev : |
1392 | 0 | pseg->prev) |
1393 | 0 | ); |
1394 | 0 | int code; |
1395 | |
|
1396 | 0 | if (alp->end.y < alp->start.y) { |
1397 | | /* fixme: The condition above causes a horizontal |
1398 | | part of a curve near an Y maximum to process twice : |
1399 | | once scanning the left spot boundary and once scanning the right one. |
1400 | | In both cases it will go to the H list. |
1401 | | However the dropout prevention logic isn't |
1402 | | sensitive to that, and such segments does not affect |
1403 | | trapezoids. Thus the resulting raster doesn't depend on that. |
1404 | | However it would be nice to improve someday. |
1405 | | */ |
1406 | 0 | remove_al(ll, alp); |
1407 | 0 | return true; |
1408 | 0 | } else if (alp->more_flattened) |
1409 | 0 | return false; |
1410 | 0 | code = init_al(alp, pseg, next, ll); |
1411 | 0 | if (code < 0) |
1412 | 0 | return code; |
1413 | 0 | if (alp->start.y > alp->end.y) { |
1414 | | /* See comment above. */ |
1415 | 0 | remove_al(ll, alp); |
1416 | 0 | return true; |
1417 | 0 | } |
1418 | 0 | alp->x_current = alp->x_next = alp->start.x; |
1419 | 0 | print_al(ll->memory, "repl", alp); |
1420 | 0 | return false; |
1421 | 0 | } |
1422 | | |
1423 | | /* |
1424 | | * Handle the case of a slanted trapezoid with adjustment. |
1425 | | * To do this exactly right requires filling a central trapezoid |
1426 | | * (or rectangle) plus two horizontal almost-rectangles. |
1427 | | */ |
1428 | | static int |
1429 | | fill_slant_adjust(const line_list *ll, |
1430 | | const active_line *flp, const active_line *alp, fixed y, fixed y1) |
1431 | 0 | { |
1432 | 0 | const fill_options * const fo = ll->fo; |
1433 | 0 | const fixed Yb = y - fo->adjust_below; |
1434 | 0 | fixed Ya = y + fo->adjust_above; |
1435 | 0 | fixed Y1b = y1 - fo->adjust_below; |
1436 | 0 | const fixed Y1a = y1 + fo->adjust_above; |
1437 | 0 | const gs_fixed_edge *plbot, *prbot, *pltop, *prtop; |
1438 | 0 | gs_fixed_edge vert_left, slant_left, vert_right, slant_right; |
1439 | 0 | int code; |
1440 | |
|
1441 | 0 | INCR(slant); |
1442 | | |
1443 | | /* Set up all the edges, even though we may not need them all. */ |
1444 | |
|
1445 | 0 | slant_left.start.x = flp->start.x - fo->adjust_left; |
1446 | 0 | slant_left.end.x = flp->end.x - fo->adjust_left; |
1447 | 0 | slant_right.start.x = alp->start.x + fo->adjust_right; |
1448 | 0 | slant_right.end.x = alp->end.x + fo->adjust_right; |
1449 | 0 | if (flp->start.x < flp->end.x) { |
1450 | 0 | vert_left.start.x = vert_left.end.x = flp->x_current - fo->adjust_left; |
1451 | 0 | vert_left.start.y = Yb, vert_left.end.y = Ya; |
1452 | 0 | vert_right.start.x = vert_right.end.x = alp->x_next + fo->adjust_right; |
1453 | 0 | vert_right.start.y = Y1b, vert_right.end.y = Y1a; |
1454 | 0 | slant_left.start.y = flp->start.y + fo->adjust_above; |
1455 | 0 | slant_left.end.y = flp->end.y + fo->adjust_above; |
1456 | 0 | slant_right.start.y = alp->start.y - fo->adjust_below; |
1457 | 0 | slant_right.end.y = alp->end.y - fo->adjust_below; |
1458 | 0 | plbot = &vert_left, prbot = &slant_right; |
1459 | 0 | pltop = &slant_left, prtop = &vert_right; |
1460 | 0 | if (TRY_TO_EXTEND_TRAP) |
1461 | 0 | { |
1462 | 0 | int x, cx, cy, dy; |
1463 | | /* Extend the slanted edges so they extend above/below the |
1464 | | * adjusted Y range. This means we could maybe use them |
1465 | | * directly. */ |
1466 | 0 | while (slant_right.end.y < Y1a) |
1467 | 0 | { |
1468 | 0 | slant_right.end.x += slant_right.end.x - slant_right.start.x; |
1469 | 0 | slant_right.end.y += slant_right.end.y - slant_right.start.y; |
1470 | 0 | } |
1471 | 0 | while (slant_left.start.y > Yb) |
1472 | 0 | { |
1473 | 0 | slant_left.start.x -= slant_left.end.x - slant_left.start.x; |
1474 | 0 | slant_left.start.y -= slant_left.end.y - slant_left.start.y; |
1475 | 0 | } |
1476 | | /* Consider the centre of the pixel above and to the right of |
1477 | | * vert_right.start. If that's inside our newly extended trap |
1478 | | * then we can't use the extended one without changing the |
1479 | | * rendered result. */ |
1480 | 0 | cy = fixed_pixround(vert_right.start.y)+fixed_half; |
1481 | 0 | cx = fixed_pixround(vert_right.start.x)+fixed_half; |
1482 | 0 | dy = slant_right.end.y-slant_right.start.y; |
1483 | 0 | x = vert_right.start.x + |
1484 | 0 | (fixed)((((int64_t)(slant_right.end.x-slant_right.start.x)) * |
1485 | 0 | ((int64_t)(cy-vert_right.start.y)) + dy - 1) / |
1486 | 0 | ((int64_t)dy)); |
1487 | 0 | if ((cy >= Y1a) || (x < cx)) |
1488 | 0 | { |
1489 | | /* We are safe to extend the trap upwards */ |
1490 | 0 | Y1b = Y1a; |
1491 | 0 | } |
1492 | | /* Consider the centre of the pixel below and to the left of |
1493 | | * vert_left.end. If that's inside our newly extended trap |
1494 | | * then we can't use the extended one without changing the |
1495 | | * rendered result. */ |
1496 | 0 | cy = fixed_pixround(vert_left.end.y)-fixed_half; |
1497 | 0 | cx = fixed_pixround(vert_left.end.x)-fixed_half; |
1498 | 0 | dy = slant_left.end.y-slant_left.start.y; |
1499 | 0 | x = vert_left.end.x - |
1500 | 0 | (fixed)((((int64_t)(slant_left.end.x-slant_left.start.x)) * |
1501 | 0 | ((int64_t)(vert_left.end.y-cy)) + dy - 1) / |
1502 | 0 | ((int64_t)dy)); |
1503 | 0 | if ((cy < Yb) || (x > cx)) |
1504 | 0 | { |
1505 | | /* We are safe to extend the trap downwards */ |
1506 | 0 | Ya = Yb; |
1507 | 0 | } |
1508 | 0 | } |
1509 | 0 | } else { |
1510 | 0 | vert_left.start.x = vert_left.end.x = flp->x_next - fo->adjust_left; |
1511 | 0 | vert_left.start.y = Y1b, vert_left.end.y = Y1a; |
1512 | 0 | vert_right.start.x = vert_right.end.x = alp->x_current + fo->adjust_right; |
1513 | 0 | vert_right.start.y = Yb, vert_right.end.y = Ya; |
1514 | 0 | slant_left.start.y = flp->start.y - fo->adjust_below; |
1515 | 0 | slant_left.end.y = flp->end.y - fo->adjust_below; |
1516 | 0 | slant_right.start.y = alp->start.y + fo->adjust_above; |
1517 | 0 | slant_right.end.y = alp->end.y + fo->adjust_above; |
1518 | 0 | plbot = &slant_left, prbot = &vert_right; |
1519 | 0 | pltop = &vert_left, prtop = &slant_right; |
1520 | 0 | if (TRY_TO_EXTEND_TRAP) |
1521 | 0 | { |
1522 | 0 | int x, cx, cy, dy; |
1523 | | /* Extend the slanted edges so they extend above/below the |
1524 | | * adjusted Y range. This means we could maybe use them |
1525 | | * directly. */ |
1526 | 0 | while (slant_left.end.y < Y1a) |
1527 | 0 | { |
1528 | 0 | slant_left.end.x += slant_left.end.x - slant_left.start.x; |
1529 | 0 | slant_left.end.y += slant_left.end.y - slant_left.start.y; |
1530 | 0 | } |
1531 | 0 | while (slant_right.start.y > Yb) |
1532 | 0 | { |
1533 | 0 | slant_right.start.x -= slant_right.end.x-slant_right.start.x; |
1534 | 0 | slant_right.start.y -= slant_right.end.y-slant_right.start.y; |
1535 | 0 | } |
1536 | | /* Consider the centre of the pixel above and to the left of |
1537 | | * vert_left.start. If that's inside our newly extended trap |
1538 | | * then we can't use the extended one without changing the |
1539 | | * rendered result. */ |
1540 | 0 | cy = fixed_pixround(vert_left.start.y)+fixed_half; |
1541 | 0 | cx = fixed_pixround(vert_left.start.x)-fixed_half; |
1542 | 0 | dy = slant_left.end.y-slant_left.start.y; |
1543 | 0 | x = vert_left.start.x - |
1544 | 0 | (fixed)((((int64_t)(slant_left.start.x-slant_left.end.x)) * |
1545 | 0 | ((int64_t)(cy-vert_left.start.y)) + dy - 1) / |
1546 | 0 | ((int64_t)dy)); |
1547 | 0 | if ((cy >= Y1a) || (x > cx)) |
1548 | 0 | { |
1549 | | /* We are safe to extend the trap upwards */ |
1550 | 0 | Y1b = Y1a; |
1551 | 0 | } |
1552 | | /* Consider the centre of the pixel below and to the left of |
1553 | | * vert_left.end. If that's inside our newly extended trap |
1554 | | * then we can't use the extended one without changing the |
1555 | | * rendered result. */ |
1556 | 0 | cy = fixed_pixround(vert_right.end.y)-fixed_half; |
1557 | 0 | cx = fixed_pixround(vert_right.end.x)-fixed_half; |
1558 | 0 | dy = slant_right.end.y-slant_right.start.y; |
1559 | 0 | x = vert_right.end.x + |
1560 | 0 | (fixed)((((int64_t)(slant_right.start.x-slant_right.end.x)) * |
1561 | 0 | ((int64_t)(vert_right.end.y-cy)) + dy - 1) / |
1562 | 0 | ((int64_t)dy)); |
1563 | 0 | if ((cy < Yb) || (x < cx)) |
1564 | 0 | { |
1565 | | /* We are safe to extend the trap downwards */ |
1566 | 0 | Ya = Yb; |
1567 | 0 | } |
1568 | 0 | } |
1569 | 0 | } |
1570 | |
|
1571 | 0 | if (Ya >= Y1b) { |
1572 | | /* |
1573 | | * The upper and lower adjustment bands overlap. |
1574 | | * Since the entire entity is less than 2 pixels high |
1575 | | * in this case, we could handle it very efficiently |
1576 | | * with no more than 2 rectangle fills, but for right now |
1577 | | * we don't attempt to do this. |
1578 | | */ |
1579 | 0 | int iYb = fixed2int_var_pixround(Yb); |
1580 | 0 | int iYa = fixed2int_var_pixround(Ya); |
1581 | 0 | int iY1b = fixed2int_var_pixround(Y1b); |
1582 | 0 | int iY1a = fixed2int_var_pixround(Y1a); |
1583 | |
|
1584 | 0 | INCR(slant_shallow); |
1585 | 0 | if (iY1b > iYb) { |
1586 | 0 | code = fo->fill_trap(fo->dev, plbot, prbot, |
1587 | 0 | Yb, Y1b, false, fo->pdevc, fo->lop); |
1588 | 0 | if (code < 0) |
1589 | 0 | return code; |
1590 | 0 | } |
1591 | 0 | if (iYa > iY1b) { |
1592 | 0 | int ix = fixed2int_var_pixround(vert_left.start.x); |
1593 | 0 | int iw = fixed2int_var_pixround(vert_right.start.x) - ix; |
1594 | |
|
1595 | 0 | code = gx_fill_rectangle_device_rop(ix, iY1b, iw, iYa - iY1b, |
1596 | 0 | fo->pdevc, fo->dev, fo->lop); |
1597 | 0 | if (code < 0) |
1598 | 0 | return code; |
1599 | 0 | } |
1600 | 0 | if (iY1a > iYa) |
1601 | 0 | code = fo->fill_trap(fo->dev, pltop, prtop, |
1602 | 0 | Ya, Y1a, false, fo->pdevc, fo->lop); |
1603 | 0 | else |
1604 | 0 | code = 0; |
1605 | 0 | } else { |
1606 | | /* |
1607 | | * Clip the trapezoid if possible. This can save a lot |
1608 | | * of work when filling paths that cross band boundaries. |
1609 | | */ |
1610 | 0 | fixed Yac; |
1611 | |
|
1612 | 0 | if (fo->pbox->p.y < Ya) { |
1613 | 0 | code = fo->fill_trap(fo->dev, plbot, prbot, |
1614 | 0 | Yb, Ya, false, fo->pdevc, fo->lop); |
1615 | 0 | if (code < 0) |
1616 | 0 | return code; |
1617 | 0 | Yac = Ya; |
1618 | 0 | } else |
1619 | 0 | Yac = fo->pbox->p.y; |
1620 | 0 | if (fo->pbox->q.y > Y1b) { |
1621 | 0 | code = fo->fill_trap(fo->dev, &slant_left, &slant_right, |
1622 | 0 | Yac, Y1b, false, fo->pdevc, fo->lop); |
1623 | 0 | if (code < 0) |
1624 | 0 | return code; |
1625 | 0 | code = fo->fill_trap(fo->dev, pltop, prtop, |
1626 | 0 | Y1b, Y1a, false, fo->pdevc, fo->lop); |
1627 | 0 | } else |
1628 | 0 | code = fo->fill_trap(fo->dev, &slant_left, &slant_right, |
1629 | 0 | Yac, fo->pbox->q.y, false, fo->pdevc, fo->lop); |
1630 | 0 | } |
1631 | 0 | return code; |
1632 | 0 | } |
1633 | | |
1634 | | /* Re-sort the x list by moving alp backward to its proper spot. */ |
1635 | | static inline void |
1636 | | resort_x_line(active_line * alp) |
1637 | 0 | { |
1638 | 0 | active_line *prev = alp->prev; |
1639 | 0 | active_line *next = alp->next; |
1640 | |
|
1641 | 0 | prev->next = next; |
1642 | 0 | if (next) |
1643 | 0 | next->prev = prev; |
1644 | 0 | while (x_order(prev, alp) > 0) { |
1645 | 0 | if_debug2('F', "[F]swap "PRI_INTPTR","PRI_INTPTR"\n", |
1646 | 0 | (intptr_t)alp, (intptr_t)prev); |
1647 | 0 | next = prev, prev = prev->prev; |
1648 | 0 | } |
1649 | 0 | alp->next = next; |
1650 | 0 | alp->prev = prev; |
1651 | | /* next might be null, if alp was in the correct spot already. */ |
1652 | 0 | if (next) |
1653 | 0 | next->prev = alp; |
1654 | | /* prev can be null if the path reaches (beyond) the extent of our coordinate space */ |
1655 | 0 | if (prev) |
1656 | 0 | prev->next = alp; |
1657 | 0 | } |
1658 | | |
1659 | | /* Move active lines by Y. */ |
1660 | | static inline int |
1661 | | move_al_by_y(line_list *ll, fixed y1) |
1662 | 0 | { |
1663 | 0 | fixed x; |
1664 | 0 | active_line *alp, *nlp; |
1665 | 0 | int code; |
1666 | |
|
1667 | 0 | for (x = min_fixed, alp = ll->x_list; alp != 0; alp = nlp) { |
1668 | 0 | bool notend = false; |
1669 | 0 | alp->x_current = alp->x_next; |
1670 | |
|
1671 | 0 | nlp = alp->next; |
1672 | 0 | if (alp->end.y == y1 && alp->more_flattened) { |
1673 | 0 | code = step_al(alp, true); |
1674 | 0 | if (code < 0) |
1675 | 0 | return code; |
1676 | 0 | alp->x_current = alp->x_next = alp->start.x; |
1677 | 0 | notend = (alp->end.y >= alp->start.y); |
1678 | 0 | } |
1679 | 0 | if (alp->end.y > y1 || notend) { |
1680 | 0 | if (alp->x_next <= x) |
1681 | 0 | resort_x_line(alp); |
1682 | 0 | else |
1683 | 0 | x = alp->x_next; |
1684 | 0 | } else { |
1685 | 0 | code = end_x_line(alp, ll, true); |
1686 | 0 | if (code < 0) |
1687 | 0 | return code; |
1688 | 0 | if (!code) { |
1689 | 0 | if (alp->x_next <= x) |
1690 | 0 | resort_x_line(alp); |
1691 | 0 | else |
1692 | 0 | x = alp->x_next; |
1693 | 0 | } |
1694 | 0 | } |
1695 | 0 | } |
1696 | 0 | return 0; |
1697 | 0 | } |
1698 | | |
1699 | | /* Process horizontal segment of curves. */ |
1700 | | static inline int |
1701 | | process_h_segments(line_list *ll, fixed y) |
1702 | 0 | { |
1703 | 0 | active_line *alp, *nlp; |
1704 | 0 | int inserted = 0; |
1705 | |
|
1706 | 0 | for (alp = ll->x_list; alp != 0; alp = nlp) { |
1707 | 0 | nlp = alp->next; |
1708 | 0 | if (alp->start.y == y && alp->end.y == y) { |
1709 | 0 | inserted = 1; |
1710 | 0 | } |
1711 | 0 | } |
1712 | 0 | return inserted; |
1713 | | /* After this should call move_al_by_y and step to the next band. */ |
1714 | 0 | } |
1715 | | |
1716 | | static inline int |
1717 | | loop_fill_trap_np(const line_list *ll, const gs_fixed_edge *le, const gs_fixed_edge *re, fixed y, fixed y1) |
1718 | 0 | { |
1719 | 0 | const fill_options * const fo = ll->fo; |
1720 | 0 | fixed ybot = max(y, fo->pbox->p.y); |
1721 | 0 | fixed ytop = min(y1, fo->pbox->q.y); |
1722 | |
|
1723 | 0 | if (ybot >= ytop) |
1724 | 0 | return 0; |
1725 | 0 | return (*fo->fill_trap) |
1726 | 0 | (fo->dev, le, re, ybot, ytop, false, fo->pdevc, fo->lop); |
1727 | 0 | } |
1728 | | |
1729 | | /* Define variants of the algorithm for filling a slanted trapezoid. */ |
1730 | | #define TEMPLATE_slant_into_trapezoids slant_into_trapezoids__fd |
1731 | 0 | #define FILL_DIRECT 1 |
1732 | | #include "gxfillts.h" |
1733 | | #undef TEMPLATE_slant_into_trapezoids |
1734 | | #undef FILL_DIRECT |
1735 | | |
1736 | | #define TEMPLATE_slant_into_trapezoids slant_into_trapezoids__nd |
1737 | 0 | #define FILL_DIRECT 0 |
1738 | | #include "gxfillts.h" |
1739 | | #undef TEMPLATE_slant_into_trapezoids |
1740 | | #undef FILL_DIRECT |
1741 | | |
1742 | | #define COVERING_PIXEL_CENTERS(y, y1, adjust_below, adjust_above)\ |
1743 | 0 | (fixed_pixround(y - adjust_below) < fixed_pixround(y1 + adjust_above)) |
1744 | | |
1745 | | /* Find an intersection within y, y1. */ |
1746 | | /* lp->x_current, lp->x_next must be set for y, y1. */ |
1747 | | static bool |
1748 | | intersect(active_line *endp, active_line *alp, fixed y, fixed y1, fixed *p_y_new) |
1749 | 0 | { |
1750 | 0 | fixed y_new, dy; |
1751 | 0 | fixed dx_old = alp->x_current - endp->x_current; |
1752 | 0 | fixed dx_den = dx_old + endp->x_next - alp->x_next; |
1753 | |
|
1754 | 0 | if (dx_den <= dx_old || dx_den == 0) |
1755 | 0 | return false; /* Intersection isn't possible. */ |
1756 | 0 | dy = y1 - y; |
1757 | 0 | if_debug3('F', "[F]cross: dy=%g, dx_old=%g, dx_new=%g\n", |
1758 | 0 | fixed2float(dy), fixed2float(dx_old), |
1759 | 0 | fixed2float(dx_den - dx_old)); |
1760 | | /* Do the computation in single precision */ |
1761 | | /* if the values are small enough. */ |
1762 | 0 | y_new = |
1763 | 0 | (((ufixed)(dy | dx_old)) < (1L << (size_of(fixed) * 4 - 1)) ? |
1764 | 0 | dy * dx_old / dx_den : |
1765 | 0 | (INCR_EXPR(mq_cross), fixed_mult_quo(dy, dx_old, dx_den))) |
1766 | 0 | + y; |
1767 | | /* The crossing value doesn't have to be */ |
1768 | | /* very accurate, but it does have to be */ |
1769 | | /* greater than y and less than y1. */ |
1770 | 0 | if_debug3('F', "[F]cross y=%g, y_new=%g, y1=%g\n", |
1771 | 0 | fixed2float(y), fixed2float(y_new), |
1772 | 0 | fixed2float(y1)); |
1773 | 0 | if (y_new <= y) { |
1774 | | /* |
1775 | | * This isn't possible. Recompute the intersection |
1776 | | * accurately. |
1777 | | */ |
1778 | 0 | fixed ys, xs0, xs1, ye, xe0, xe1, dy, dx0, dx1; |
1779 | |
|
1780 | 0 | INCR(cross_slow); |
1781 | 0 | if (endp->start.y < alp->start.y) |
1782 | 0 | ys = alp->start.y, |
1783 | 0 | xs0 = AL_X_AT_Y(endp, ys), xs1 = alp->start.x; |
1784 | 0 | else |
1785 | 0 | ys = endp->start.y, |
1786 | 0 | xs0 = endp->start.x, xs1 = AL_X_AT_Y(alp, ys); |
1787 | 0 | if (endp->end.y > alp->end.y) |
1788 | 0 | ye = alp->end.y, |
1789 | 0 | xe0 = AL_X_AT_Y(endp, ye), xe1 = alp->end.x; |
1790 | 0 | else |
1791 | 0 | ye = endp->end.y, |
1792 | 0 | xe0 = endp->end.x, xe1 = AL_X_AT_Y(alp, ye); |
1793 | 0 | dy = ye - ys; |
1794 | 0 | dx0 = xe0 - xs0; |
1795 | 0 | dx1 = xe1 - xs1; |
1796 | | /* We need xs0 + cross * dx0 == xs1 + cross * dx1. */ |
1797 | 0 | if (dx0 == dx1) { |
1798 | | /* The two lines are coincident. Do nothing. */ |
1799 | 0 | y_new = y1; |
1800 | 0 | } else { |
1801 | 0 | double cross = (double)(xs0 - xs1) / (dx1 - dx0); |
1802 | |
|
1803 | 0 | y_new = (fixed)(ys + cross * dy); |
1804 | 0 | if (y_new <= y) { |
1805 | | /* |
1806 | | * This can only happen through some kind of |
1807 | | * numeric disaster, but we have to check. |
1808 | | */ |
1809 | 0 | INCR(cross_low); |
1810 | 0 | y_new = y + fixed_epsilon; |
1811 | 0 | } |
1812 | 0 | } |
1813 | 0 | } |
1814 | 0 | *p_y_new = y_new; |
1815 | 0 | return true; |
1816 | 0 | } |
1817 | | |
1818 | | static inline void |
1819 | | set_x_next(active_line *endp, active_line *alp, fixed x) |
1820 | 0 | { |
1821 | 0 | while(endp != alp) { |
1822 | 0 | endp->x_next = x; |
1823 | 0 | endp = endp->next; |
1824 | 0 | } |
1825 | 0 | } |
1826 | | |
1827 | | static inline int |
1828 | | coord_weight(const active_line *alp) |
1829 | 0 | { |
1830 | 0 | return 1 + min(any_abs((int)((int64_t)alp->diff.y * 8 / alp->diff.x)), 256); |
1831 | 0 | } |
1832 | | |
1833 | | /* Find intersections of active lines within the band. |
1834 | | Intersect and reorder them, and correct the bund top. */ |
1835 | | static void |
1836 | | intersect_al(line_list *ll, fixed y, fixed *y_top, int draw, bool all_bands) |
1837 | 0 | { |
1838 | 0 | fixed x = min_fixed, y1 = *y_top; |
1839 | 0 | active_line *alp, *stopx = NULL; |
1840 | 0 | active_line *endp = NULL; |
1841 | |
|
1842 | 0 | if (y == y1) { |
1843 | | /* Rather the intersection algorithm can handle this case with |
1844 | | retrieving x_next equal to x_current, |
1845 | | we bypass it for safety reason. |
1846 | | */ |
1847 | 0 | } else if (draw >= 0 || all_bands) { |
1848 | | /* |
1849 | | * Loop invariants: |
1850 | | * alp = endp->next; |
1851 | | * for all lines lp from stopx up to alp, |
1852 | | * lp->x_next = AL_X_AT_Y(lp, y1). |
1853 | | */ |
1854 | 0 | for (alp = stopx = ll->x_list; |
1855 | 0 | INCR_EXPR(find_y), alp != 0; |
1856 | 0 | endp = alp, alp = alp->next |
1857 | 0 | ) { |
1858 | 0 | fixed nx = AL_X_AT_Y(alp, y1); |
1859 | 0 | fixed y_new; |
1860 | |
|
1861 | 0 | alp->x_next = nx; |
1862 | | /* Check for intersecting lines. */ |
1863 | 0 | if (nx >= x) |
1864 | 0 | x = nx; |
1865 | 0 | else if (alp->x_current >= endp->x_current && |
1866 | 0 | intersect(endp, alp, y, y1, &y_new)) { |
1867 | 0 | if (y_new <= y1) { |
1868 | 0 | stopx = endp; |
1869 | 0 | y1 = y_new; |
1870 | 0 | if (endp->diff.x == 0) |
1871 | 0 | nx = endp->start.x; |
1872 | 0 | else if (alp->diff.x == 0) |
1873 | 0 | nx = alp->start.x; |
1874 | 0 | else { |
1875 | 0 | fixed nx0 = AL_X_AT_Y(endp, y1); |
1876 | 0 | fixed nx1 = AL_X_AT_Y(alp, y1); |
1877 | 0 | if (nx0 != nx1) { |
1878 | | /* Different results due to arithmetic errors. |
1879 | | Choose an imtermediate point. |
1880 | | We don't like to use floating numbners here, |
1881 | | but the code with int64_t isn't much better. */ |
1882 | 0 | int64_t w0 = coord_weight(endp); |
1883 | 0 | int64_t w1 = coord_weight(alp); |
1884 | |
|
1885 | 0 | nx = (fixed)((w0 * nx0 + w1 * nx1) / (w0 + w1)); |
1886 | 0 | } else |
1887 | 0 | nx = nx0; |
1888 | 0 | } |
1889 | 0 | endp->x_next = alp->x_next = nx; /* Ensure same X. */ |
1890 | | /* Can't guarantee same x for triple intersections here. |
1891 | | Will take care below */ |
1892 | 0 | } |
1893 | 0 | if (nx > x) |
1894 | 0 | x = nx; |
1895 | 0 | } |
1896 | 0 | } |
1897 | | /* Recompute next_x for lines before the intersection. */ |
1898 | 0 | for (alp = ll->x_list; alp != stopx; alp = alp->next) |
1899 | 0 | alp->x_next = AL_X_AT_Y(alp, y1); |
1900 | | /* Ensure X monotonity (particularly imporoves triple intersections). */ |
1901 | 0 | if (ll->x_list != NULL) { |
1902 | 0 | for (;;) { |
1903 | 0 | fixed x1; |
1904 | 0 | double sx = 0xbaadf00d; /* 'fixed' can overflow. We operate 7-bytes integers. */ |
1905 | 0 | int k = 0xbaadf00d, n; |
1906 | |
|
1907 | 0 | endp = ll->x_list; |
1908 | 0 | x1 = endp->x_next; |
1909 | 0 | for (alp = endp->next; alp != NULL; x1 = alp->x_next, alp = alp->next) |
1910 | 0 | if (alp->x_next < x1) |
1911 | 0 | break; |
1912 | 0 | if (alp == NULL) |
1913 | 0 | break; |
1914 | 0 | x1 = endp->x_next; |
1915 | 0 | n = 0; |
1916 | 0 | for (alp = endp->next; alp != NULL; alp = alp->next) { |
1917 | 0 | x = alp->x_next; |
1918 | 0 | if (x < x1) { |
1919 | 0 | if (n == 0) { |
1920 | 0 | if (endp->diff.x == 0) { |
1921 | 0 | k = -1; |
1922 | 0 | sx = x1; |
1923 | 0 | } else { |
1924 | 0 | k = coord_weight(endp); |
1925 | 0 | sx = (double)x1 * k; |
1926 | 0 | } |
1927 | 0 | n = 1; |
1928 | 0 | } |
1929 | 0 | n++; |
1930 | 0 | if (alp->diff.x == 0) { |
1931 | | /* Vertical lines have a higher priority. */ |
1932 | 0 | if (k <= -1) { |
1933 | 0 | sx += x; |
1934 | 0 | k--; |
1935 | 0 | } else { |
1936 | 0 | k = -1; |
1937 | 0 | sx = x; |
1938 | 0 | } |
1939 | 0 | } else if (k > 0) { |
1940 | 0 | int w = coord_weight(alp); |
1941 | |
|
1942 | 0 | sx += (double)x * w; |
1943 | 0 | k += w; |
1944 | 0 | } |
1945 | 0 | } else { |
1946 | 0 | if (n > 1) { |
1947 | 0 | k = any_abs(k); |
1948 | 0 | set_x_next(endp, alp, (fixed)((sx + k / 2) / k)); |
1949 | 0 | } |
1950 | 0 | x1 = alp->x_next; |
1951 | 0 | n = 0; |
1952 | 0 | endp = alp; |
1953 | 0 | } |
1954 | 0 | } |
1955 | 0 | if (n > 1) { |
1956 | 0 | k = any_abs(k); |
1957 | 0 | set_x_next(endp, alp, (fixed)((sx + k / 2) / k)); |
1958 | 0 | } |
1959 | 0 | } |
1960 | 0 | } |
1961 | 0 | } else { |
1962 | | /* Recompute next_x for lines before the intersection. */ |
1963 | 0 | for (alp = ll->x_list; alp != stopx; alp = alp->next) |
1964 | 0 | alp->x_next = AL_X_AT_Y(alp, y1); |
1965 | 0 | } |
1966 | | #ifdef DEBUG |
1967 | | if (gs_debug_c('F')) { |
1968 | | dmlprintf1(ll->memory, "[F]after loop: y1=%f\n", fixed2float(y1)); |
1969 | | print_line_list(ll->memory, ll->x_list); |
1970 | | } |
1971 | | #endif |
1972 | 0 | *y_top = y1; |
1973 | 0 | } |
1974 | | |
1975 | | /* ---------------- Trapezoid filling loop ---------------- */ |
1976 | | |
1977 | | /* Generate specialized algorythms for the most important cases : */ |
1978 | | |
1979 | | /* The smart winding counter advance operator : */ |
1980 | 0 | #define signed_eo(a) ((a) < 0 ? -((a) & 1) : (a) > 0 ? ((a) & 1) : 0) |
1981 | | #define ADVANCE_WINDING(inside, alp, ll) \ |
1982 | 0 | { int k = alp->contour_count; \ |
1983 | 0 | int v = ll->windings[k]; \ |
1984 | 0 | inside -= signed_eo(v); \ |
1985 | 0 | v = ll->windings[k] += alp->direction; \ |
1986 | 0 | inside += signed_eo(v); \ |
1987 | 0 | } |
1988 | | |
1989 | 0 | #define IS_SPOTAN 0 |
1990 | 0 | #define SMART_WINDING 1 |
1991 | 0 | #define FILL_ADJUST 0 |
1992 | 0 | #define FILL_DIRECT 1 |
1993 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_fd_sw |
1994 | | #include "gxfilltr.h" |
1995 | | #undef IS_SPOTAN |
1996 | | #undef SMART_WINDING |
1997 | | #undef FILL_ADJUST |
1998 | | #undef FILL_DIRECT |
1999 | | #undef TEMPLATE_spot_into_trapezoids |
2000 | | |
2001 | 0 | #define IS_SPOTAN 0 |
2002 | 0 | #define SMART_WINDING 1 |
2003 | 0 | #define FILL_ADJUST 0 |
2004 | 0 | #define FILL_DIRECT 0 |
2005 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_nd_sw |
2006 | | #include "gxfilltr.h" |
2007 | | #undef IS_SPOTAN |
2008 | | #undef SMART_WINDING |
2009 | | #undef FILL_ADJUST |
2010 | | #undef FILL_DIRECT |
2011 | | #undef TEMPLATE_spot_into_trapezoids |
2012 | | |
2013 | | #undef signed_eo |
2014 | | #undef ADVANCE_WINDING |
2015 | | /* The simple winding counter advance operator : */ |
2016 | 0 | #define ADVANCE_WINDING(inside, alp, ll) inside += alp->direction |
2017 | | |
2018 | 0 | #define IS_SPOTAN 1 |
2019 | 0 | #define SMART_WINDING 0 |
2020 | 0 | #define FILL_ADJUST 0 |
2021 | 0 | #define FILL_DIRECT 1 |
2022 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__spotan |
2023 | | #include "gxfilltr.h" |
2024 | | #undef IS_SPOTAN |
2025 | | #undef SMART_WINDING |
2026 | | #undef FILL_ADJUST |
2027 | | #undef FILL_DIRECT |
2028 | | #undef TEMPLATE_spot_into_trapezoids |
2029 | | |
2030 | 0 | #define IS_SPOTAN 0 |
2031 | 0 | #define SMART_WINDING 0 |
2032 | 0 | #define FILL_ADJUST 1 |
2033 | 0 | #define FILL_DIRECT 1 |
2034 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__aj_fd |
2035 | | #include "gxfilltr.h" |
2036 | | #undef IS_SPOTAN |
2037 | | #undef SMART_WINDING |
2038 | | #undef FILL_ADJUST |
2039 | | #undef FILL_DIRECT |
2040 | | #undef TEMPLATE_spot_into_trapezoids |
2041 | | |
2042 | 0 | #define IS_SPOTAN 0 |
2043 | 0 | #define SMART_WINDING 0 |
2044 | 0 | #define FILL_ADJUST 1 |
2045 | 0 | #define FILL_DIRECT 0 |
2046 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__aj_nd |
2047 | | #include "gxfilltr.h" |
2048 | | #undef IS_SPOTAN |
2049 | | #undef SMART_WINDING |
2050 | | #undef FILL_ADJUST |
2051 | | #undef FILL_DIRECT |
2052 | | #undef TEMPLATE_spot_into_trapezoids |
2053 | | |
2054 | 0 | #define IS_SPOTAN 0 |
2055 | 0 | #define SMART_WINDING 0 |
2056 | 0 | #define FILL_ADJUST 0 |
2057 | 0 | #define FILL_DIRECT 1 |
2058 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_fd |
2059 | | #include "gxfilltr.h" |
2060 | | #undef IS_SPOTAN |
2061 | | #undef SMART_WINDING |
2062 | | #undef FILL_ADJUST |
2063 | | #undef FILL_DIRECT |
2064 | | #undef TEMPLATE_spot_into_trapezoids |
2065 | | |
2066 | 0 | #define IS_SPOTAN 0 |
2067 | 0 | #define SMART_WINDING 0 |
2068 | 0 | #define FILL_ADJUST 0 |
2069 | 0 | #define FILL_DIRECT 0 |
2070 | | #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_nd |
2071 | | #include "gxfilltr.h" |
2072 | | #undef IS_SPOTAN |
2073 | | #undef SMART_WINDING |
2074 | | #undef FILL_ADJUST |
2075 | | #undef FILL_DIRECT |
2076 | | #undef TEMPLATE_spot_into_trapezoids |
2077 | | |
2078 | | #undef ADVANCE_WINDING |
2079 | | |
2080 | | /* Main filling loop. Takes lines off of y_list and adds them to */ |
2081 | | /* x_list as needed. band_mask limits the size of each band, */ |
2082 | | /* by requiring that ((y1 - 1) & band_mask) == (y0 & band_mask). */ |
2083 | | static int |
2084 | | spot_into_trapezoids(line_list *ll, fixed band_mask) |
2085 | 0 | { |
2086 | | /* For better performance, choose a specialized algorithm : */ |
2087 | 0 | const fill_options * const fo = ll->fo; |
2088 | |
|
2089 | 0 | if (fo->is_spotan) |
2090 | 0 | return spot_into_trapezoids__spotan(ll, band_mask); |
2091 | 0 | if (fo->adjust_below | fo->adjust_above | fo->adjust_left | fo->adjust_right) { |
2092 | 0 | if (fo->fill_direct) |
2093 | 0 | return spot_into_trapezoids__aj_fd(ll, band_mask); |
2094 | 0 | else |
2095 | 0 | return spot_into_trapezoids__aj_nd(ll, band_mask); |
2096 | 0 | } else if (ll->windings != NULL) { |
2097 | 0 | if (fo->fill_direct) |
2098 | 0 | return spot_into_trapezoids__nj_fd_sw(ll, band_mask); |
2099 | 0 | else |
2100 | 0 | return spot_into_trapezoids__nj_nd_sw(ll, band_mask); |
2101 | 0 | } else { |
2102 | 0 | if (fo->fill_direct) |
2103 | 0 | return spot_into_trapezoids__nj_fd(ll, band_mask); |
2104 | 0 | else |
2105 | 0 | return spot_into_trapezoids__nj_nd(ll, band_mask); |
2106 | 0 | } |
2107 | 0 | } |
2108 | | |
2109 | | /* ---------------- Range list management ---------------- */ |
2110 | | |
2111 | | /* |
2112 | | * We originally thought we would store fixed values in coordinate ranges, |
2113 | | * but it turned out we want to store integers. |
2114 | | */ |
2115 | | typedef int coord_value_t; |
2116 | 0 | #define MIN_COORD_VALUE min_int |
2117 | 0 | #define MAX_COORD_VALUE max_int |
2118 | | /* |
2119 | | * The invariant for coord_range_ts in a coord_range_list_t: |
2120 | | * if prev == 0: |
2121 | | * next != 0 |
2122 | | * rmin == rmax == MIN_COORD_VALUE |
2123 | | * else if next == 0: |
2124 | | * prev != 0 |
2125 | | * rmin == rmax == MAX_COORD_VALUE |
2126 | | * else |
2127 | | * rmin < rmax |
2128 | | * if prev != 0: |
2129 | | * prev->next == this |
2130 | | * prev->rmax < rmin |
2131 | | * if next != 0: |
2132 | | * next->prev = this |
2133 | | * next->rmin > rmax |
2134 | | * A coord_range_list_t has a boundary element at each end to avoid having |
2135 | | * to test for null pointers in the insertion loop. The boundary elements |
2136 | | * are the only empty ranges. |
2137 | | */ |
2138 | | typedef struct coord_range_s coord_range_t; |
2139 | | struct coord_range_s { |
2140 | | coord_value_t rmin, rmax; |
2141 | | coord_range_t *prev, *next; |
2142 | | coord_range_t *alloc_next; |
2143 | | }; |
2144 | | /* We declare coord_range_ts as simple because they only exist transiently. */ |
2145 | | gs_private_st_simple(st_coord_range, coord_range_t, "coord_range_t"); |
2146 | | |
2147 | | typedef struct coord_range_list_s { |
2148 | | gs_memory_t *memory; |
2149 | | struct rl_ { |
2150 | | coord_range_t *first, *next, *limit; |
2151 | | } local; |
2152 | | coord_range_t *allocated; |
2153 | | coord_range_t *freed; |
2154 | | coord_range_t *current; |
2155 | | coord_range_t first, last; |
2156 | | } coord_range_list_t; |
2157 | | |
2158 | | static coord_range_t * |
2159 | | range_alloc(coord_range_list_t *pcrl) |
2160 | 0 | { |
2161 | 0 | coord_range_t *pcr; |
2162 | |
|
2163 | 0 | if (pcrl->freed) { |
2164 | 0 | pcr = pcrl->freed; |
2165 | 0 | pcrl->freed = pcr->next; |
2166 | 0 | } else if (pcrl->local.next < pcrl->local.limit) |
2167 | 0 | pcr = pcrl->local.next++; |
2168 | 0 | else { |
2169 | 0 | pcr = gs_alloc_struct(pcrl->memory, coord_range_t, &st_coord_range, |
2170 | 0 | "range_alloc"); |
2171 | 0 | if (pcr == 0) |
2172 | 0 | return 0; |
2173 | 0 | pcr->alloc_next = pcrl->allocated; |
2174 | 0 | pcrl->allocated = pcr; |
2175 | 0 | } |
2176 | 0 | return pcr; |
2177 | 0 | } |
2178 | | |
2179 | | static void |
2180 | | range_delete(coord_range_list_t *pcrl, coord_range_t *pcr) |
2181 | 0 | { |
2182 | 0 | if_debug3('Q', "[Qr]delete "PRI_INTPTR": [%d,%d)\n", |
2183 | 0 | (intptr_t)pcr, pcr->rmin, pcr->rmax); |
2184 | 0 | pcr->prev->next = pcr->next; |
2185 | 0 | pcr->next->prev = pcr->prev; |
2186 | 0 | pcr->next = pcrl->freed; |
2187 | 0 | pcrl->freed = pcr; |
2188 | 0 | } |
2189 | | |
2190 | | static void |
2191 | | range_list_clear(coord_range_list_t *pcrl) |
2192 | 0 | { |
2193 | 0 | if_debug0('Q', "[Qr]clearing\n"); |
2194 | 0 | pcrl->first.next = &pcrl->last; |
2195 | 0 | pcrl->last.prev = &pcrl->first; |
2196 | 0 | pcrl->current = &pcrl->last; |
2197 | 0 | } |
2198 | | |
2199 | | /* ------ "Public" procedures ------ */ |
2200 | | |
2201 | | /* Initialize a range list. We require num_local >= 2. */ |
2202 | | static void range_list_clear(coord_range_list_t *); |
2203 | | static void |
2204 | | range_list_init(coord_range_list_t *pcrl, coord_range_t *pcr_local, |
2205 | | int num_local, gs_memory_t *mem) |
2206 | 0 | { |
2207 | 0 | pcrl->memory = mem; |
2208 | 0 | pcrl->local.first = pcrl->local.next = pcr_local; |
2209 | 0 | pcrl->local.limit = pcr_local + num_local; |
2210 | 0 | pcrl->allocated = pcrl->freed = 0; |
2211 | 0 | pcrl->first.rmin = pcrl->first.rmax = MIN_COORD_VALUE; |
2212 | 0 | pcrl->first.prev = 0; |
2213 | 0 | pcrl->last.rmin = pcrl->last.rmax = MAX_COORD_VALUE; |
2214 | 0 | pcrl->last.next = 0; |
2215 | 0 | range_list_clear(pcrl); |
2216 | 0 | } |
2217 | | |
2218 | | /* Reset an initialized range list to the empty state. */ |
2219 | | static void |
2220 | | range_list_reset(coord_range_list_t *pcrl) |
2221 | 0 | { |
2222 | 0 | if (pcrl->first.next != &pcrl->last) { |
2223 | 0 | pcrl->last.prev->next = pcrl->freed; |
2224 | 0 | pcrl->freed = pcrl->first.next; |
2225 | 0 | range_list_clear(pcrl); |
2226 | 0 | } |
2227 | 0 | } |
2228 | | |
2229 | | /* |
2230 | | * Move the cursor to the beginning of a range list, in the belief that |
2231 | | * the next added ranges will roughly parallel the existing ones. |
2232 | | * (Only affects performance, not function.) |
2233 | | */ |
2234 | | static inline void |
2235 | | range_list_rescan(coord_range_list_t *pcrl) |
2236 | 0 | { |
2237 | 0 | pcrl->current = pcrl->first.next; |
2238 | 0 | } |
2239 | | |
2240 | | /* Free a range list. */ |
2241 | | static void |
2242 | | range_list_free(coord_range_list_t *pcrl) |
2243 | 0 | { |
2244 | 0 | coord_range_t *pcr; |
2245 | |
|
2246 | 0 | while ((pcr = pcrl->allocated) != 0) { |
2247 | 0 | coord_range_t *next = pcr->alloc_next; |
2248 | |
|
2249 | 0 | gs_free_object(pcrl->memory, pcr, "range_list_free"); |
2250 | 0 | pcrl->allocated = next; |
2251 | 0 | } |
2252 | 0 | } |
2253 | | |
2254 | | /* Add a range. */ |
2255 | | static int |
2256 | | range_list_add(coord_range_list_t *pcrl, coord_value_t rmin, coord_value_t rmax) |
2257 | 0 | { |
2258 | 0 | coord_range_t *pcr = pcrl->current; |
2259 | |
|
2260 | 0 | if (rmin >= rmax) |
2261 | 0 | return 0; |
2262 | | /* |
2263 | | * Usually, ranges are added in increasing order within each scan line, |
2264 | | * and overlapping ranges don't differ much. Optimize accordingly. |
2265 | | */ |
2266 | 0 | top: |
2267 | 0 | if (rmax < pcr->rmin) { |
2268 | 0 | if (rmin > pcr->prev->rmax) |
2269 | 0 | goto ins; |
2270 | 0 | pcr = pcr->prev; |
2271 | 0 | goto top; |
2272 | 0 | } |
2273 | 0 | if (rmin > pcr->rmax) { |
2274 | 0 | pcr = pcr->next; |
2275 | 0 | if (rmax < pcr->rmin) |
2276 | 0 | goto ins; |
2277 | 0 | goto top; |
2278 | 0 | } |
2279 | | /* |
2280 | | * Now we know that (rmin,rmax) overlaps (pcr->rmin,pcr->rmax). |
2281 | | * Don't delete or merge into the special min and max ranges. |
2282 | | */ |
2283 | 0 | while (rmin <= pcr->prev->rmax) { |
2284 | | /* Merge the range below pcr into this one. */ |
2285 | 0 | if (!pcr->prev->prev) |
2286 | 0 | break; /* don't merge into the min range */ |
2287 | 0 | pcr->rmin = pcr->prev->rmin; |
2288 | 0 | range_delete(pcrl, pcr->prev); |
2289 | 0 | } |
2290 | 0 | while (rmax >= pcr->next->rmin) { |
2291 | | /* Merge the range above pcr into this one. */ |
2292 | 0 | if (!pcr->next->next) |
2293 | 0 | break; /* don't merge into the max range */ |
2294 | 0 | pcr->rmax = pcr->next->rmax; |
2295 | 0 | range_delete(pcrl, pcr->next); |
2296 | 0 | } |
2297 | | /* |
2298 | | * Now we know that the union of (rmin,rmax) and (pcr->rmin,pcr->rmax) |
2299 | | * doesn't overlap or abut either adjacent range, except that it may |
2300 | | * abut if the adjacent range is the special min or max range. |
2301 | | */ |
2302 | 0 | if (rmin < pcr->rmin) { |
2303 | 0 | if_debug3('Q', "[Qr]update "PRI_INTPTR" => [%d,%d)\n", |
2304 | 0 | (intptr_t)pcr, rmin, pcr->rmax); |
2305 | 0 | pcr->rmin = rmin; |
2306 | 0 | } |
2307 | 0 | if (rmax > pcr->rmax) { |
2308 | 0 | if_debug3('Q', "[Qr]update "PRI_INTPTR" => [%d,%d)\n", |
2309 | 0 | (intptr_t)pcr, pcr->rmin, rmax); |
2310 | 0 | pcr->rmax = rmax; |
2311 | 0 | } |
2312 | 0 | pcrl->current = pcr->next; |
2313 | 0 | return 0; |
2314 | 0 | ins: |
2315 | | /* Insert a new range below pcr. */ |
2316 | 0 | { |
2317 | 0 | coord_range_t *prev = range_alloc(pcrl); |
2318 | |
|
2319 | 0 | if (prev == 0) |
2320 | 0 | return_error(gs_error_VMerror); |
2321 | 0 | if_debug3('Q', "[Qr]insert "PRI_INTPTR": [%d,%d)\n", (intptr_t)prev, rmin, rmax); |
2322 | 0 | prev->rmin = rmin, prev->rmax = rmax; |
2323 | 0 | (prev->prev = pcr->prev)->next = prev; |
2324 | 0 | prev->next = pcr; |
2325 | 0 | pcr->prev = prev; |
2326 | 0 | } |
2327 | 0 | pcrl->current = pcr; |
2328 | 0 | return 0; |
2329 | 0 | } |
2330 | | |
2331 | | /* |
2332 | | * Merge regions for active segments starting at a given Y, or all active |
2333 | | * segments, up to the end of the sampling band or the end of the segment, |
2334 | | * into the range list. |
2335 | | */ |
2336 | | static int |
2337 | | merge_ranges(coord_range_list_t *pcrl, const line_list *ll, fixed y_min, fixed y_top) |
2338 | 0 | { |
2339 | 0 | active_line *alp, *nlp; |
2340 | 0 | int code = 0; |
2341 | |
|
2342 | 0 | range_list_rescan(pcrl); |
2343 | 0 | for (alp = ll->x_list; alp != 0 && code >= 0; alp = nlp) { |
2344 | 0 | fixed x0 = alp->x_current, x1, xt; |
2345 | 0 | bool forth = (alp->direction == DIR_UP || !alp->fi.curve); |
2346 | 0 | fixed xe = (forth ? alp->fi.x3 : alp->fi.x0); |
2347 | 0 | fixed ye = (forth ? alp->fi.y3 : alp->fi.y0); |
2348 | |
|
2349 | 0 | nlp = alp->next; |
2350 | 0 | if (alp->start.y < y_min) |
2351 | 0 | continue; |
2352 | 0 | if (alp->monotonic_x && alp->monotonic_y && ye <= y_top) { |
2353 | 0 | x1 = xe; |
2354 | 0 | if (x0 > x1) |
2355 | 0 | xt = x0, x0 = x1, x1 = xt; |
2356 | 0 | code = range_list_add(pcrl, |
2357 | 0 | fixed2int_pixround(x0 - ll->fo->adjust_left), |
2358 | 0 | fixed2int_rounded(x1 + ll->fo->adjust_right)); |
2359 | 0 | alp->more_flattened = false; /* Skip all the segments left. */ |
2360 | 0 | } else { |
2361 | 0 | x1 = x0; |
2362 | 0 | for (;;) { |
2363 | 0 | if (alp->end.y <= y_top) |
2364 | 0 | xt = alp->end.x; |
2365 | 0 | else |
2366 | 0 | xt = AL_X_AT_Y(alp, y_top); |
2367 | 0 | x0 = min(x0, xt); |
2368 | 0 | x1 = max(x1, xt); |
2369 | 0 | if (!alp->more_flattened || alp->end.y > y_top) |
2370 | 0 | break; |
2371 | 0 | code = step_al(alp, true); |
2372 | 0 | if (code < 0) |
2373 | 0 | return code; |
2374 | 0 | if (alp->end.y < alp->start.y) { |
2375 | 0 | remove_al(ll, alp); /* End of a monotonic part of a curve. */ |
2376 | 0 | break; |
2377 | 0 | } |
2378 | 0 | } |
2379 | 0 | code = range_list_add(pcrl, |
2380 | 0 | fixed2int_pixround(x0 - ll->fo->adjust_left), |
2381 | 0 | fixed2int_rounded(x1 + ll->fo->adjust_right)); |
2382 | 0 | } |
2383 | |
|
2384 | 0 | } |
2385 | 0 | return code; |
2386 | 0 | } |
2387 | | |
2388 | | /* ---------------- Scan line filling loop ---------------- */ |
2389 | | |
2390 | | /* defina specializations of the scanline algorithm. */ |
2391 | | |
2392 | 0 | #define FILL_DIRECT 1 |
2393 | | #define TEMPLATE_spot_into_scanlines spot_into_scan_lines_fd |
2394 | | #include "gxfillsl.h" |
2395 | | #undef FILL_DIRECT |
2396 | | #undef TEMPLATE_spot_into_scanlines |
2397 | | |
2398 | 0 | #define FILL_DIRECT 0 |
2399 | | #define TEMPLATE_spot_into_scanlines spot_into_scan_lines_nd |
2400 | | #include "gxfillsl.h" |
2401 | | #undef FILL_DIRECT |
2402 | | #undef TEMPLATE_spot_into_scanlines |
2403 | | |
2404 | | static int |
2405 | | spot_into_scan_lines(line_list *ll, fixed band_mask) |
2406 | 0 | { |
2407 | 0 | if (ll->fo->fill_direct) |
2408 | 0 | return spot_into_scan_lines_fd(ll, band_mask); |
2409 | 0 | else |
2410 | 0 | return spot_into_scan_lines_nd(ll, band_mask); |
2411 | 0 | } |