/src/ghostpdl/base/gxfillsl.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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 | | /* Configurable algorithm for filling a path by scanlines. */ |
18 | | |
19 | | /* |
20 | | * Since we need several statically defined variants of this agorithm, |
21 | | * we store it in .h file and include it several times into gxfill.c . |
22 | | * Configuration macros (template arguments) are : |
23 | | * |
24 | | * FILL_DIRECT - See LOOP_FILL_RECTANGLE_DIRECT. |
25 | | * TEMPLATE_spot_into_scanlines - the name of the procedure to generate. |
26 | | */ |
27 | | |
28 | | #if defined(TEMPLATE_spot_into_scanlines) && defined(INCR) && defined(LOOP_FILL_RECTANGLE_DIRECT) |
29 | | |
30 | | static int |
31 | | TEMPLATE_spot_into_scanlines (line_list *ll, fixed band_mask) |
32 | 0 | { |
33 | 0 | const fill_options fo = *ll->fo; |
34 | 0 | active_line *yll = ll->y_list; |
35 | 0 | fixed y_limit = fo.ymax; |
36 | | /* |
37 | | * The meaning of adjust_below (B) and adjust_above (A) is that the |
38 | | * pixels that would normally be painted at coordinate Y get "smeared" |
39 | | * to coordinates Y-B through Y+A-epsilon, inclusive. This is |
40 | | * equivalent to saying that the pixels actually painted at coordinate Y |
41 | | * are those contributed by scan lines Y-A+epsilon through Y+B, |
42 | | * inclusive. (A = B = 0 is a special case, equivalent to B = 0, A = |
43 | | * epsilon.) |
44 | | */ |
45 | 0 | fixed y_frac_min = |
46 | 0 | (fo.adjust_above == fixed_0 ? fixed_half : |
47 | 0 | fixed_half + fixed_epsilon - fo.adjust_above); |
48 | 0 | fixed y_frac_max = |
49 | 0 | fixed_half + fo.adjust_below; |
50 | 0 | int y0 = fixed2int(min_fixed); |
51 | 0 | fixed y_bot = min_fixed; /* normally int2fixed(y0) + y_frac_min */ |
52 | 0 | fixed y_top = min_fixed; /* normally int2fixed(y0) + y_frac_max */ |
53 | 0 | fixed y = min_fixed; |
54 | 0 | coord_range_list_t rlist; |
55 | 0 | coord_range_t rlocal[MAX_LOCAL_ACTIVE]; |
56 | 0 | int code = 0; |
57 | |
|
58 | 0 | if (yll == 0) /* empty list */ |
59 | 0 | return 0; |
60 | 0 | range_list_init(&rlist, rlocal, countof(rlocal), ll->memory); |
61 | 0 | ll->x_list = 0; |
62 | 0 | ll->x_head.x_current = min_fixed; /* stop backward scan */ |
63 | 0 | do { |
64 | 0 | active_line *alp, *nlp; |
65 | 0 | fixed x; |
66 | 0 | bool new_band; |
67 | |
|
68 | 0 | INCR(iter); |
69 | |
|
70 | 0 | code = move_al_by_y(ll, y); /* Skip horizontal pieces. */ |
71 | 0 | if (code < 0) |
72 | 0 | return code; |
73 | | /* |
74 | | * Find the next sampling point, either the bottom of a sampling |
75 | | * band or a line start. |
76 | | */ |
77 | | |
78 | 0 | if (ll->x_list == 0) |
79 | 0 | y = (yll == 0 ? ll->y_break : yll->start.y); |
80 | 0 | else { |
81 | 0 | y = y_bot + fixed_1; |
82 | 0 | if (yll != 0) |
83 | 0 | y = min(y, yll->start.y); |
84 | 0 | for (alp = ll->x_list; alp != 0; alp = alp->next) { |
85 | 0 | fixed yy = max(alp->fi.y3, alp->fi.y0); |
86 | |
|
87 | 0 | yy = max(yy, alp->end.y); /* Non-monotonic curves may have an inner extreme. */ |
88 | 0 | y = min(y, yy); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | | /* Move newly active lines from y to x list. */ |
93 | |
|
94 | 0 | while (yll != 0 && yll->start.y == y) { |
95 | 0 | active_line *ynext = yll->next; /* insert smashes next/prev links */ |
96 | |
|
97 | 0 | if (yll->direction == DIR_HORIZONTAL) { |
98 | 0 | insert_h_new(yll, ll); |
99 | 0 | } else |
100 | 0 | insert_x_new(yll, ll); |
101 | 0 | yll = ynext; |
102 | 0 | } |
103 | | |
104 | | /* Update active lines to y. */ |
105 | |
|
106 | 0 | x = min_fixed; |
107 | 0 | for (alp = ll->x_list; alp != 0; alp = nlp) { |
108 | 0 | fixed nx; |
109 | |
|
110 | 0 | nlp = alp->next; |
111 | 0 | e:if (alp->end.y <= y || alp->start.y == alp->end.y) { |
112 | 0 | if (end_x_line(alp, ll, true)) |
113 | 0 | continue; |
114 | 0 | if (alp->more_flattened) |
115 | 0 | if (alp->end.y <= y || alp->start.y == alp->end.y) { |
116 | 0 | code = step_al(alp, true); |
117 | 0 | if (code < 0) |
118 | 0 | return code; |
119 | 0 | } |
120 | 0 | goto e; |
121 | 0 | } |
122 | 0 | nx = alp->x_current = (alp->start.y >= y ? alp->start.x : AL_X_AT_Y(alp, y)); |
123 | 0 | if (nx < x) { |
124 | | /* Move this line backward in the list. */ |
125 | 0 | active_line *ilp = alp; |
126 | |
|
127 | 0 | while (nx < (ilp = ilp->prev)->x_current) |
128 | 0 | DO_NOTHING; |
129 | | /* Now ilp->x_current <= nx < ilp->next->x_cur. */ |
130 | 0 | alp->prev->next = alp->next; |
131 | 0 | if (alp->next) |
132 | 0 | alp->next->prev = alp->prev; |
133 | 0 | if (ilp->next) |
134 | 0 | ilp->next->prev = alp; |
135 | 0 | alp->next = ilp->next; |
136 | 0 | ilp->next = alp; |
137 | 0 | alp->prev = ilp; |
138 | 0 | continue; |
139 | 0 | } |
140 | 0 | x = nx; |
141 | 0 | } |
142 | | |
143 | 0 | if (y > y_top || y >= y_limit) { |
144 | | /* We're beyond the end of the previous sampling band. */ |
145 | 0 | const coord_range_t *pcr; |
146 | | |
147 | | /* Fill the ranges for y0. */ |
148 | |
|
149 | 0 | for (pcr = rlist.first.next; pcr != &rlist.last; |
150 | 0 | pcr = pcr->next |
151 | 0 | ) { |
152 | 0 | int x0 = pcr->rmin, x1 = pcr->rmax; |
153 | |
|
154 | 0 | if_debug4m('Q', ll->memory, "[Qr]draw "PRI_INTPTR": [%d,%d),%d\n", |
155 | 0 | (intptr_t)pcr, x0, x1, y0); |
156 | 0 | code = LOOP_FILL_RECTANGLE_DIRECT(&fo, x0, y0, x1 - x0, 1); |
157 | 0 | if_debug3m('F', ll->memory, "[F]drawing [%d:%d),%d\n", x0, x1, y0); |
158 | 0 | if (code < 0) |
159 | 0 | goto done; |
160 | 0 | } |
161 | 0 | range_list_reset(&rlist); |
162 | | |
163 | | /* Check whether we've reached the maximum y. */ |
164 | |
|
165 | 0 | if (y >= y_limit) |
166 | 0 | break; |
167 | | |
168 | | /* Reset the sampling band. */ |
169 | | |
170 | 0 | y0 = fixed2int(y); |
171 | 0 | if (fixed_fraction(y) < y_frac_min) |
172 | 0 | --y0; |
173 | 0 | y_bot = int2fixed(y0) + y_frac_min; |
174 | 0 | y_top = int2fixed(y0) + y_frac_max; |
175 | 0 | new_band = true; |
176 | 0 | } else |
177 | 0 | new_band = false; |
178 | | |
179 | 0 | if (y <= y_top) { |
180 | | /* |
181 | | * We're within the same Y pixel. Merge regions for segments |
182 | | * starting here (at y), up to y_top or the end of the segment. |
183 | | * If this is the first sampling within the band, run the |
184 | | * fill/eofill algorithm. |
185 | | */ |
186 | 0 | fixed y_min; |
187 | |
|
188 | 0 | if (new_band) { |
189 | 0 | int inside = 0; |
190 | |
|
191 | 0 | INCR(band); |
192 | 0 | for (alp = ll->x_list; alp != 0; alp = alp->next) { |
193 | 0 | int x0 = fixed2int_pixround(alp->x_current - fo.adjust_left); |
194 | |
|
195 | 0 | for (;;) { |
196 | | /* We're inside a filled region. */ |
197 | 0 | print_al(ll->memory, "step", alp); |
198 | 0 | INCR(band_step); |
199 | 0 | inside += alp->direction; |
200 | 0 | if (!INSIDE_PATH_P(inside, fo.rule)) |
201 | 0 | break; |
202 | | /* |
203 | | * Since we're dealing with closed paths, the test |
204 | | * for alp == 0 shouldn't be needed, but we may have |
205 | | * omitted lines that are to the right of the |
206 | | * clipping region. |
207 | | */ |
208 | 0 | if ((alp = alp->next) == 0) |
209 | 0 | goto out; |
210 | 0 | } |
211 | | /* We just went from inside to outside, so fill the region. */ |
212 | 0 | code = range_list_add(&rlist, x0, |
213 | 0 | fixed2int_rounded(alp->x_current + |
214 | 0 | fo.adjust_right)); |
215 | 0 | if (code < 0) |
216 | 0 | goto done; |
217 | 0 | } |
218 | 0 | out: |
219 | 0 | y_min = min_fixed; |
220 | 0 | } else |
221 | 0 | y_min = y; |
222 | | |
223 | | /* Process horizontal segments */ |
224 | | |
225 | 0 | for (alp = ll->h_list0; alp != NULL; alp = alp->next) { |
226 | 0 | fixed x0 = min(alp->start.x, alp->end.x); |
227 | 0 | fixed x1 = max(alp->start.x, alp->end.x); |
228 | |
|
229 | 0 | code = range_list_add(&rlist, fixed2int_rounded(x0 - fo.adjust_left), |
230 | 0 | fixed2int_rounded(x1 + fo.adjust_right)); |
231 | 0 | if (code < 0) |
232 | 0 | goto done; |
233 | 0 | } |
234 | | |
235 | 0 | code = merge_ranges(&rlist, ll, y_min, y_top); |
236 | 0 | } /* else y < y_bot + 1, do nothing */ |
237 | 0 | ll->h_list0 = NULL; |
238 | 0 | } while (code >= 0); |
239 | 0 | done: |
240 | 0 | range_list_free(&rlist); |
241 | 0 | return code; |
242 | 0 | } Unexecuted instantiation: gxfill.c:spot_into_scan_lines_fd Unexecuted instantiation: gxfill.c:spot_into_scan_lines_nd |
243 | | |
244 | | #else |
245 | | int dummy; |
246 | | #endif |