/src/ghostpdl/base/gxfilltr.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 decomposing a spot into trapezoids. */ |
18 | | |
19 | | /* |
20 | | * Since we need several statically defined variants of this algorithm, |
21 | | * we store it in .h file and include it several times into gxfill.c . |
22 | | * Configuration macros (template arguments) are : |
23 | | * |
24 | | * IS_SPOTAN - is the target device a spot analyzer ("spotan"). |
25 | | * SMART_WINDING - even-odd filling rule for each contour independently. |
26 | | * FILL_ADJUST - fill adjustment is not zero |
27 | | * FILL_DIRECT - See LOOP_FILL_RECTANGLE_DIRECT. |
28 | | * TEMPLATE_spot_into_trapezoids - the name of the procedure to generate. |
29 | | * ADVANCE_WINDING(inside, alp, ll) - a macro for advancing the winding counter. |
30 | | * INSIDE_PATH_P(inside, rule) - a macro for checking the winding rule. |
31 | | */ |
32 | | |
33 | | #if defined(TEMPLATE_spot_into_trapezoids) && defined(INCR) && defined(FILL_ADJUST) && defined(LOOP_FILL_RECTANGLE_DIRECT) && defined(COVERING_PIXEL_CENTERS) |
34 | | |
35 | | /* ---------------- Trapezoid decomposition loop ---------------- */ |
36 | | |
37 | | /* Takes lines off of y_list and adds them to */ |
38 | | /* x_list as needed. band_mask limits the size of each band, */ |
39 | | /* by requiring that ((y1 - 1) & band_mask) == (y0 & band_mask). */ |
40 | | static int |
41 | | TEMPLATE_spot_into_trapezoids (line_list *ll, fixed band_mask) |
42 | 0 | { |
43 | 0 | const fill_options fo = *ll->fo; |
44 | 0 | int rule = fo.rule; |
45 | 0 | const fixed y_limit = fo.ymax; |
46 | 0 | active_line *yll = ll->y_list; |
47 | 0 | fixed y; |
48 | 0 | int code; |
49 | 0 | const bool all_bands = fo.is_spotan; |
50 | |
|
51 | 0 | if (yll == 0) |
52 | 0 | return 0; /* empty list */ |
53 | 0 | y = yll->start.y; /* first Y value */ |
54 | 0 | ll->x_list = 0; |
55 | 0 | ll->x_head.x_current = min_fixed; /* stop backward scan */ |
56 | 0 | while (1) { |
57 | 0 | fixed y1; |
58 | 0 | active_line *alp; |
59 | 0 | bool covering_pixel_centers; |
60 | |
|
61 | 0 | INCR(iter); |
62 | | /* Move newly active lines from y to x list. */ |
63 | 0 | while (yll != 0 && yll->start.y == y) { |
64 | 0 | active_line *ynext = yll->next; /* insert smashes next/prev links */ |
65 | |
|
66 | 0 | ll->y_list = ynext; |
67 | 0 | if (ll->y_line == yll) |
68 | 0 | ll->y_line = ynext; |
69 | 0 | if (ynext != NULL) |
70 | 0 | ynext->prev = NULL; |
71 | 0 | if (yll->direction == DIR_HORIZONTAL) { |
72 | | /* |
73 | | * This is a hack to make sure that isolated horizontal |
74 | | * lines get stroked. |
75 | | */ |
76 | 0 | int yi = fixed2int_pixround(y - (!FILL_ADJUST ? 0 : fo.adjust_below)); |
77 | 0 | int xi, wi; |
78 | |
|
79 | 0 | if (yll->start.x <= yll->end.x) { |
80 | 0 | xi = fixed2int_pixround(yll->start.x - (!FILL_ADJUST ? 0 : fo.adjust_left)); |
81 | 0 | wi = fixed2int_pixround(yll->end.x + (!FILL_ADJUST ? 0 : fo.adjust_right)) - xi; |
82 | 0 | } else { |
83 | 0 | xi = fixed2int_pixround(yll->end.x - (!FILL_ADJUST ? 0 : fo.adjust_left)); |
84 | 0 | wi = fixed2int_pixround(yll->start.x + (!FILL_ADJUST ? 0 : fo.adjust_right)) - xi; |
85 | 0 | } |
86 | 0 | code = LOOP_FILL_RECTANGLE_DIRECT(&fo, xi, yi, wi, 1); |
87 | 0 | if (code < 0) |
88 | 0 | return code; |
89 | 0 | } else |
90 | 0 | insert_x_new(yll, ll); |
91 | 0 | yll = ynext; |
92 | 0 | } |
93 | | /* Mustn't leave by Y before process_h_segments. */ |
94 | 0 | if (ll->x_list == 0) { /* No active lines, skip to next start */ |
95 | 0 | if (yll == 0) |
96 | 0 | break; /* no lines left */ |
97 | | /* We don't close margin set here because the next set |
98 | | * may fall into same window. */ |
99 | 0 | y = yll->start.y; |
100 | 0 | ll->h_list1 = ll->h_list0; |
101 | 0 | ll->h_list0 = 0; |
102 | 0 | continue; |
103 | 0 | } |
104 | | /* Find the next evaluation point. */ |
105 | | /* Start by finding the smallest y value */ |
106 | | /* at which any currently active line ends */ |
107 | | /* (or the next to-be-active line begins). */ |
108 | 0 | y1 = (yll != 0 ? yll->start.y : ll->y_break); |
109 | | /* Make sure we don't exceed the maximum band height. */ |
110 | 0 | { |
111 | 0 | fixed y_band = y | ~band_mask; |
112 | |
|
113 | 0 | if (y1 > y_band) |
114 | 0 | y1 = y_band + 1; |
115 | 0 | } |
116 | 0 | for (alp = ll->x_list; alp != 0; alp = alp->next) { |
117 | 0 | if (alp->end.y < y1) |
118 | 0 | y1 = alp->end.y; |
119 | 0 | } |
120 | | # ifdef DEBUG |
121 | | if (gs_debug_c('F')) { |
122 | | dmlprintf2(ll->memory, "[F]before loop: y=%f y1=%f:\n", |
123 | | fixed2float(y), fixed2float(y1)); |
124 | | print_line_list(ll->memory, ll->x_list); |
125 | | } |
126 | | # endif |
127 | 0 | if (y == y1) { |
128 | 0 | code = process_h_segments(ll, y); |
129 | 0 | if (code < 0) |
130 | 0 | return code; |
131 | 0 | { int code1 = move_al_by_y(ll, y1); |
132 | 0 | if (code1 < 0) |
133 | 0 | return code1; |
134 | 0 | } |
135 | 0 | if (code > 0) { |
136 | 0 | yll = ll->y_list; /* add_y_line_aux in process_h_segments changes it. */ |
137 | 0 | continue; |
138 | 0 | } |
139 | |
|
140 | 0 | } |
141 | 0 | if (y >= y_limit) |
142 | 0 | break; |
143 | | /* Now look for line intersections before y1. */ |
144 | 0 | covering_pixel_centers = COVERING_PIXEL_CENTERS(y, y1, |
145 | 0 | (!FILL_ADJUST ? 0 : fo.adjust_below), |
146 | 0 | (!FILL_ADJUST ? 0 : fo.adjust_above)); |
147 | 0 | if (y != y1) { |
148 | 0 | intersect_al(ll, y, &y1, (covering_pixel_centers ? 1 : -1), all_bands); /* May change y1. */ |
149 | 0 | covering_pixel_centers = COVERING_PIXEL_CENTERS(y, y1, |
150 | 0 | (!FILL_ADJUST ? 0 : fo.adjust_below), |
151 | 0 | (!FILL_ADJUST ? 0 : fo.adjust_above)); |
152 | 0 | } |
153 | | /* Fill a multi-trapezoid band for the active lines. */ |
154 | 0 | if (covering_pixel_centers || all_bands) { |
155 | 0 | int inside = 0; |
156 | 0 | active_line *flp = NULL; |
157 | |
|
158 | 0 | if (SMART_WINDING) |
159 | 0 | memset(ll->windings, 0, sizeof(ll->windings[0]) * ll->contour_count); |
160 | 0 | INCR(band); |
161 | | /* Generate trapezoids */ |
162 | 0 | for (alp = ll->x_list; alp != 0; alp = alp->next) { |
163 | 0 | int code; |
164 | |
|
165 | 0 | print_al(ll->memory, "step", alp); |
166 | 0 | INCR(band_step); |
167 | 0 | if (!INSIDE_PATH_P(inside, rule)) { /* i.e., outside */ |
168 | 0 | ADVANCE_WINDING(inside, alp, ll); |
169 | 0 | if (INSIDE_PATH_P(inside, rule)) /* about to go in */ |
170 | 0 | flp = alp; |
171 | 0 | continue; |
172 | 0 | } |
173 | | /* We're inside a region being filled. */ |
174 | 0 | ADVANCE_WINDING(inside, alp, ll); |
175 | 0 | if (INSIDE_PATH_P(inside, rule)) /* not about to go out */ |
176 | 0 | continue; |
177 | | /* We just went from inside to outside, |
178 | | chech whether we'll immediately go inside. */ |
179 | 0 | if (alp->next != NULL && |
180 | 0 | alp->x_current == alp->next->x_current && |
181 | 0 | alp->x_next == alp->next->x_next) { |
182 | | /* If the next trapezoid contacts this one, unite them. |
183 | | This simplifies data for the spot analyzer |
184 | | and reduces the number of trapezoids in the rasterization. |
185 | | Note that the topology possibly isn't exactly such |
186 | | as we generate by this uniting : |
187 | | Due to arithmetic errors in x_current, x_next |
188 | | we can unite things, which really are not contacting. |
189 | | But this level of the topology precision is enough for |
190 | | the glyph grid fitting. |
191 | | Also note that |
192 | | while a rasterization with dropout prevention |
193 | | it may cause a shift when choosing a pixel |
194 | | to paint with a narrow trapezoid. */ |
195 | 0 | alp = alp->next; |
196 | 0 | ADVANCE_WINDING(inside, alp, ll); |
197 | 0 | continue; |
198 | 0 | } |
199 | | /* We just went from inside to outside, so fill the region. */ |
200 | 0 | INCR(band_fill); |
201 | 0 | if (FILL_ADJUST && !(flp->end.x == flp->start.x && alp->end.x == alp->start.x) && |
202 | 0 | (fo.adjust_below | fo.adjust_above) != 0) { |
203 | 0 | if (FILL_DIRECT) |
204 | 0 | code = slant_into_trapezoids__fd(ll, flp, alp, y, y1); |
205 | 0 | else |
206 | 0 | code = slant_into_trapezoids__nd(ll, flp, alp, y, y1); |
207 | 0 | } else { |
208 | 0 | fixed ybot = max(y, fo.pbox->p.y); |
209 | 0 | fixed ytop = min(y1, fo.pbox->q.y); |
210 | |
|
211 | 0 | if (IS_SPOTAN) { |
212 | | /* We can't pass data through the device interface because |
213 | | we need to pass segment pointers. We're unhappy of that. */ |
214 | 0 | code = gx_san_trap_store((gx_device_spot_analyzer *)fo.dev, |
215 | 0 | y, y1, flp->x_current, alp->x_current, flp->x_next, alp->x_next, |
216 | 0 | flp->pseg, alp->pseg, flp->direction, alp->direction); |
217 | 0 | } else { |
218 | 0 | if (flp->end.x == flp->start.x && alp->end.x == alp->start.x) { |
219 | 0 | if (FILL_ADJUST) { |
220 | 0 | ybot = max(y - fo.adjust_below, fo.pbox->p.y); |
221 | 0 | ytop = min(y1 + fo.adjust_above, fo.pbox->q.y); |
222 | 0 | } |
223 | 0 | if (ytop > ybot) { |
224 | 0 | int yi = fixed2int_pixround(ybot); |
225 | 0 | int hi = fixed2int_pixround(ytop) - yi; |
226 | 0 | int xli = fixed2int_var_pixround(flp->end.x - (!FILL_ADJUST ? 0 : fo.adjust_left)); |
227 | 0 | int xi = fixed2int_var_pixround(alp->end.x + (!FILL_ADJUST ? 0 : fo.adjust_right)); |
228 | |
|
229 | | #ifdef FILL_ZERO_WIDTH |
230 | | if ( xli == xi && FILL_ADJUST && |
231 | | (fo.adjust_right | fo.adjust_left) != 0 ) { |
232 | | #else |
233 | 0 | if (0) { |
234 | 0 | #endif |
235 | | /* |
236 | | * The scan is empty but we should paint something |
237 | | * against a dropout. Choose one of two pixels which |
238 | | * is closer to the "axis". |
239 | | */ |
240 | 0 | fixed xx = int2fixed(xli); |
241 | |
|
242 | 0 | if (xx - flp->end.x < alp->end.x - xx) |
243 | 0 | ++xi; |
244 | 0 | else |
245 | 0 | --xli; |
246 | 0 | } |
247 | 0 | code = LOOP_FILL_RECTANGLE_DIRECT(&fo, xli, yi, xi - xli, hi); |
248 | 0 | } else |
249 | 0 | code = 0; |
250 | 0 | } else if (ybot < ytop) { |
251 | 0 | gs_fixed_edge le, re; |
252 | |
|
253 | 0 | le.start = flp->start; |
254 | 0 | le.end = flp->end; |
255 | 0 | re.start = alp->start; |
256 | 0 | re.end = alp->end; |
257 | 0 | code = fo.fill_trap(fo.dev, |
258 | 0 | &le, &re, ybot, ytop, false, fo.pdevc, fo.lop); |
259 | 0 | } else |
260 | 0 | code = 0; |
261 | 0 | } |
262 | 0 | } |
263 | 0 | if (code < 0) |
264 | 0 | return code; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | code = move_al_by_y(ll, y1); |
268 | 0 | if (code < 0) |
269 | 0 | return code; |
270 | 0 | ll->h_list1 = ll->h_list0; |
271 | 0 | ll->h_list0 = 0; |
272 | 0 | y = y1; |
273 | 0 | } |
274 | 0 | return 0; |
275 | 0 | } Unexecuted instantiation: gxfill.c:spot_into_trapezoids__spotan Unexecuted instantiation: gxfill.c:spot_into_trapezoids__aj_fd Unexecuted instantiation: gxfill.c:spot_into_trapezoids__aj_nd Unexecuted instantiation: gxfill.c:spot_into_trapezoids__nj_fd_sw Unexecuted instantiation: gxfill.c:spot_into_trapezoids__nj_nd_sw Unexecuted instantiation: gxfill.c:spot_into_trapezoids__nj_fd Unexecuted instantiation: gxfill.c:spot_into_trapezoids__nj_nd |
276 | | |
277 | | #else |
278 | | int dummy; |
279 | | #endif |