1*593dc095SDavid du Colombier /* Copyright (C) 1989-2003 artofcode LLC. All rights reserved.
27dd7cddfSDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
57dd7cddfSDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: gxfill.c,v 1.122 2005/08/22 14:29:18 igor Exp $ */
18*593dc095SDavid du Colombier /* A topological spot decomposition algorithm with dropout prevention. */
19*593dc095SDavid du Colombier /*
20*593dc095SDavid du Colombier This is a dramaticly reorganized and improved revision of the
21*593dc095SDavid du Colombier filling algorithm, iwhich was nitially coded by Henry Stiles.
22*593dc095SDavid du Colombier The main improvements are:
23*593dc095SDavid du Colombier 1. A dropout prevention for character fill.
24*593dc095SDavid du Colombier 2. The spot topology analysys support
25*593dc095SDavid du Colombier for True Type grid fitting.
26*593dc095SDavid du Colombier 3. Fixed the contiguty of a spot covering
27*593dc095SDavid du Colombier for shading fills with no dropouts.
28*593dc095SDavid du Colombier */
29*593dc095SDavid du Colombier /* See PSEUDO_RASTERISATION and "pseudo_rasterization".
30*593dc095SDavid du Colombier about the dropout previntion logics. */
31*593dc095SDavid du Colombier /* See is_spotan about the spot topology analyzis support. */
32*593dc095SDavid du Colombier /* Also defining lower-level path filling procedures */
33*593dc095SDavid du Colombier
347dd7cddfSDavid du Colombier #include "gx.h"
357dd7cddfSDavid du Colombier #include "gserrors.h"
367dd7cddfSDavid du Colombier #include "gsstruct.h"
377dd7cddfSDavid du Colombier #include "gxfixed.h"
387dd7cddfSDavid du Colombier #include "gxdevice.h"
397dd7cddfSDavid du Colombier #include "gzpath.h"
407dd7cddfSDavid du Colombier #include "gzcpath.h"
417dd7cddfSDavid du Colombier #include "gxdcolor.h"
427dd7cddfSDavid du Colombier #include "gxhttile.h"
437dd7cddfSDavid du Colombier #include "gxistate.h"
447dd7cddfSDavid du Colombier #include "gxpaint.h" /* for prototypes */
45*593dc095SDavid du Colombier #include "gxfdrop.h"
46*593dc095SDavid du Colombier #include "gxfill.h"
473ff48bf5SDavid du Colombier #include "gsptype2.h"
48*593dc095SDavid du Colombier #include "gdevddrw.h"
49*593dc095SDavid du Colombier #include "gzspotan.h" /* Only for gx_san_trap_store. */
50*593dc095SDavid du Colombier #include "memory_.h"
51*593dc095SDavid du Colombier #include "stdint_.h"
52*593dc095SDavid du Colombier #include "vdtrace.h"
533ff48bf5SDavid du Colombier /*
54*593dc095SDavid du Colombier #include "gxfilltr.h" - Do not remove this comment. "gxfilltr.h" is included below.
55*593dc095SDavid du Colombier #include "gxfillsl.h" - Do not remove this comment. "gxfillsl.h" is included below.
56*593dc095SDavid du Colombier #include "gxfillts.h" - Do not remove this comment. "gxfillts.h" is included below.
573ff48bf5SDavid du Colombier */
583ff48bf5SDavid du Colombier
597dd7cddfSDavid du Colombier #ifdef DEBUG
60*593dc095SDavid du Colombier /* Define the statistics structure instance. */
61*593dc095SDavid du Colombier stats_fill_t stats_fill;
627dd7cddfSDavid du Colombier #endif
637dd7cddfSDavid du Colombier
64*593dc095SDavid du Colombier /* A predicate for spot insideness. */
65*593dc095SDavid du Colombier /* rule = -1 for winding number rule, i.e. */
66*593dc095SDavid du Colombier /* we are inside if the winding number is non-zero; */
67*593dc095SDavid du Colombier /* rule = 1 for even-odd rule, i.e. */
68*593dc095SDavid du Colombier /* we are inside if the winding number is odd. */
69*593dc095SDavid du Colombier #define INSIDE_PATH_P(inside, rule) ((inside & rule) != 0)
70*593dc095SDavid du Colombier
71*593dc095SDavid du Colombier
723ff48bf5SDavid du Colombier /* ---------------- Active line management ---------------- */
733ff48bf5SDavid du Colombier
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier /*
767dd7cddfSDavid du Colombier * Define the ordering criterion for active lines that overlap in Y.
777dd7cddfSDavid du Colombier * Return -1, 0, or 1 if lp1 precedes, coincides with, or follows lp2.
787dd7cddfSDavid du Colombier *
797dd7cddfSDavid du Colombier * The lines' x_current values are correct for some Y value that crosses
807dd7cddfSDavid du Colombier * both of them and that is not both the start of one and the end of the
817dd7cddfSDavid du Colombier * other. (Neither line is horizontal.) We want the ordering at this
827dd7cddfSDavid du Colombier * Y value, or, of the x_current values are equal, greater Y values
837dd7cddfSDavid du Colombier * (if any: this Y value might be the end of both lines).
847dd7cddfSDavid du Colombier */
853ff48bf5SDavid du Colombier private int
x_order(const active_line * lp1,const active_line * lp2)867dd7cddfSDavid du Colombier x_order(const active_line *lp1, const active_line *lp2)
877dd7cddfSDavid du Colombier {
887dd7cddfSDavid du Colombier bool s1;
897dd7cddfSDavid du Colombier
907dd7cddfSDavid du Colombier INCR(order);
917dd7cddfSDavid du Colombier if (lp1->x_current < lp2->x_current)
927dd7cddfSDavid du Colombier return -1;
937dd7cddfSDavid du Colombier else if (lp1->x_current > lp2->x_current)
947dd7cddfSDavid du Colombier return 1;
957dd7cddfSDavid du Colombier /*
967dd7cddfSDavid du Colombier * We need to compare the slopes of the lines. Start by
977dd7cddfSDavid du Colombier * checking one fast case, where the slopes have opposite signs.
987dd7cddfSDavid du Colombier */
997dd7cddfSDavid du Colombier if ((s1 = lp1->start.x < lp1->end.x) != (lp2->start.x < lp2->end.x))
1007dd7cddfSDavid du Colombier return (s1 ? 1 : -1);
1017dd7cddfSDavid du Colombier /*
1027dd7cddfSDavid du Colombier * We really do have to compare the slopes. Fortunately, this isn't
1037dd7cddfSDavid du Colombier * needed very often. We want the sign of the comparison
1047dd7cddfSDavid du Colombier * dx1/dy1 - dx2/dy2, or (since we know dy1 and dy2 are positive)
1053ff48bf5SDavid du Colombier * dx1 * dy2 - dx2 * dy1. In principle, we can't simply do this using
1067dd7cddfSDavid du Colombier * doubles, since we need complete accuracy and doubles don't have
1073ff48bf5SDavid du Colombier * enough fraction bits. However, with the usual 20+12-bit fixeds and
1083ff48bf5SDavid du Colombier * 64-bit doubles, both of the factors would have to exceed 2^15
1093ff48bf5SDavid du Colombier * device space pixels for the result to be inaccurate, so we
1103ff48bf5SDavid du Colombier * simply disregard this possibility. ****** FIX SOMEDAY ******
1117dd7cddfSDavid du Colombier */
1127dd7cddfSDavid du Colombier INCR(slow_order);
1137dd7cddfSDavid du Colombier {
1143ff48bf5SDavid du Colombier fixed dx1 = lp1->end.x - lp1->start.x,
1153ff48bf5SDavid du Colombier dy1 = lp1->end.y - lp1->start.y;
1163ff48bf5SDavid du Colombier fixed dx2 = lp2->end.x - lp2->start.x,
1173ff48bf5SDavid du Colombier dy2 = lp2->end.y - lp2->start.y;
1183ff48bf5SDavid du Colombier double diff = (double)dx1 * dy2 - (double)dx2 * dy1;
1197dd7cddfSDavid du Colombier
1203ff48bf5SDavid du Colombier return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
1217dd7cddfSDavid du Colombier }
1227dd7cddfSDavid du Colombier }
1237dd7cddfSDavid du Colombier
1247dd7cddfSDavid du Colombier /*
1257dd7cddfSDavid du Colombier * The active_line structure isn't really simple, but since its instances
1267dd7cddfSDavid du Colombier * only exist temporarily during a fill operation, we don't have to
1277dd7cddfSDavid du Colombier * worry about a garbage collection occurring.
1287dd7cddfSDavid du Colombier */
1297dd7cddfSDavid du Colombier gs_private_st_simple(st_active_line, active_line, "active_line");
1307dd7cddfSDavid du Colombier
1317dd7cddfSDavid du Colombier #ifdef DEBUG
1327dd7cddfSDavid du Colombier /* Internal procedures for printing and checking active lines. */
1337dd7cddfSDavid du Colombier private void
print_active_line(const char * label,const active_line * alp)1347dd7cddfSDavid du Colombier print_active_line(const char *label, const active_line * alp)
1357dd7cddfSDavid du Colombier {
1367dd7cddfSDavid du Colombier dlprintf5("[f]%s 0x%lx(%d): x_current=%f x_next=%f\n",
1377dd7cddfSDavid du Colombier label, (ulong) alp, alp->direction,
1387dd7cddfSDavid du Colombier fixed2float(alp->x_current), fixed2float(alp->x_next));
1397dd7cddfSDavid du Colombier dlprintf5(" start=(%f,%f) pt_end=0x%lx(%f,%f)\n",
1407dd7cddfSDavid du Colombier fixed2float(alp->start.x), fixed2float(alp->start.y),
1417dd7cddfSDavid du Colombier (ulong) alp->pseg,
1427dd7cddfSDavid du Colombier fixed2float(alp->end.x), fixed2float(alp->end.y));
1437dd7cddfSDavid du Colombier dlprintf2(" prev=0x%lx next=0x%lx\n",
1447dd7cddfSDavid du Colombier (ulong) alp->prev, (ulong) alp->next);
1457dd7cddfSDavid du Colombier }
1467dd7cddfSDavid du Colombier private void
print_line_list(const active_line * flp)1477dd7cddfSDavid du Colombier print_line_list(const active_line * flp)
1487dd7cddfSDavid du Colombier {
1497dd7cddfSDavid du Colombier const active_line *lp;
1507dd7cddfSDavid du Colombier
1517dd7cddfSDavid du Colombier for (lp = flp; lp != 0; lp = lp->next) {
1527dd7cddfSDavid du Colombier fixed xc = lp->x_current, xn = lp->x_next;
1537dd7cddfSDavid du Colombier
1547dd7cddfSDavid du Colombier dlprintf3("[f]0x%lx(%d): x_current/next=%g",
1557dd7cddfSDavid du Colombier (ulong) lp, lp->direction,
1567dd7cddfSDavid du Colombier fixed2float(xc));
1577dd7cddfSDavid du Colombier if (xn != xc)
1587dd7cddfSDavid du Colombier dprintf1("/%g", fixed2float(xn));
1597dd7cddfSDavid du Colombier dputc('\n');
1607dd7cddfSDavid du Colombier }
1617dd7cddfSDavid du Colombier }
1623ff48bf5SDavid du Colombier inline private void
print_al(const char * label,const active_line * alp)1633ff48bf5SDavid du Colombier print_al(const char *label, const active_line * alp)
1643ff48bf5SDavid du Colombier {
1653ff48bf5SDavid du Colombier if (gs_debug_c('F'))
1663ff48bf5SDavid du Colombier print_active_line(label, alp);
1673ff48bf5SDavid du Colombier }
1687dd7cddfSDavid du Colombier #else
1697dd7cddfSDavid du Colombier #define print_al(label,alp) DO_NOTHING
1707dd7cddfSDavid du Colombier #endif
1717dd7cddfSDavid du Colombier
172*593dc095SDavid du Colombier private inline bool
is_spotan_device(gx_device * dev)173*593dc095SDavid du Colombier is_spotan_device(gx_device * dev)
174*593dc095SDavid du Colombier {
175*593dc095SDavid du Colombier return dev->memory != NULL &&
176*593dc095SDavid du Colombier gs_object_type(dev->memory, dev) == &st_device_spot_analyzer;
177*593dc095SDavid du Colombier }
1787dd7cddfSDavid du Colombier
1797dd7cddfSDavid du Colombier /* Forward declarations */
180*593dc095SDavid du Colombier private void free_line_list(line_list *);
181*593dc095SDavid du Colombier private int add_y_list(gx_path *, line_list *);
182*593dc095SDavid du Colombier private int add_y_line_aux(const segment * prev_lp, const segment * lp,
183*593dc095SDavid du Colombier const gs_fixed_point *curr, const gs_fixed_point *prev, int dir, line_list *ll);
184*593dc095SDavid du Colombier private void insert_x_new(active_line *, line_list *);
185*593dc095SDavid du Colombier private bool end_x_line(active_line *, const line_list *, bool);
186*593dc095SDavid du Colombier private void step_al(active_line *alp, bool move_iterator);
1877dd7cddfSDavid du Colombier
188*593dc095SDavid du Colombier
189*593dc095SDavid du Colombier #define FILL_LOOP_PROC(proc) int proc(line_list *, fixed band_mask)
190*593dc095SDavid du Colombier private FILL_LOOP_PROC(spot_into_scan_lines);
191*593dc095SDavid du Colombier private FILL_LOOP_PROC(spot_into_trapezoids);
1927dd7cddfSDavid du Colombier
1937dd7cddfSDavid du Colombier /*
1947dd7cddfSDavid du Colombier * This is the general path filling algorithm.
195*593dc095SDavid du Colombier * It uses the center-of-pixel rule for filling
196*593dc095SDavid du Colombier * (except for pseudo_rasterization - see below).
1977dd7cddfSDavid du Colombier * We can implement Microsoft's upper-left-corner-of-pixel rule
1987dd7cddfSDavid du Colombier * by subtracting (0.5, 0.5) from all the coordinates in the path.
1997dd7cddfSDavid du Colombier *
2007dd7cddfSDavid du Colombier * The adjust parameters are a hack for keeping regions
2017dd7cddfSDavid du Colombier * from coming out too faint: they specify an amount by which to expand
2027dd7cddfSDavid du Colombier * the sides of every filled region.
2037dd7cddfSDavid du Colombier * Setting adjust = fixed_half is supposed to produce the effect of Adobe's
2047dd7cddfSDavid du Colombier * any-part-of-pixel rule, but it doesn't quite, because of the
2057dd7cddfSDavid du Colombier * closed/open interval rule for regions. We detect this as a special case
2067dd7cddfSDavid du Colombier * and do the slightly ugly things necessary to make it work.
2077dd7cddfSDavid du Colombier */
2087dd7cddfSDavid du Colombier
209*593dc095SDavid du Colombier /* Initialize the line list for a path. */
210*593dc095SDavid du Colombier private inline void
init_line_list(line_list * ll,gs_memory_t * mem)211*593dc095SDavid du Colombier init_line_list(line_list *ll, gs_memory_t * mem)
212*593dc095SDavid du Colombier {
213*593dc095SDavid du Colombier ll->memory = mem;
214*593dc095SDavid du Colombier ll->active_area = 0;
215*593dc095SDavid du Colombier ll->next_active = ll->local_active;
216*593dc095SDavid du Colombier ll->limit = ll->next_active + MAX_LOCAL_ACTIVE;
217*593dc095SDavid du Colombier ll->close_count = 0;
218*593dc095SDavid du Colombier ll->y_list = 0;
219*593dc095SDavid du Colombier ll->y_line = 0;
220*593dc095SDavid du Colombier ll->h_list0 = ll->h_list1 = 0;
221*593dc095SDavid du Colombier ll->margin_set0.margin_list = ll->margin_set1.margin_list = 0;
222*593dc095SDavid du Colombier ll->margin_set0.margin_touched = ll->margin_set1.margin_touched = 0;
223*593dc095SDavid du Colombier ll->margin_set0.y = ll->margin_set1.y = 0; /* A stub against indeterminism. Don't use it. */
224*593dc095SDavid du Colombier ll->free_margin_list = 0;
225*593dc095SDavid du Colombier ll->local_margin_alloc_count = 0;
226*593dc095SDavid du Colombier ll->margin_set0.sect = ll->local_section0;
227*593dc095SDavid du Colombier ll->margin_set1.sect = ll->local_section1;
228*593dc095SDavid du Colombier /* Do not initialize ll->bbox_left, ll->bbox_width - they were set in advance. */
229*593dc095SDavid du Colombier INCR(fill);
230*593dc095SDavid du Colombier }
231*593dc095SDavid du Colombier
232*593dc095SDavid du Colombier
233*593dc095SDavid du Colombier /* Unlink any line_close segments added temporarily. */
234*593dc095SDavid du Colombier private inline void
unclose_path(gx_path * ppath,int count)235*593dc095SDavid du Colombier unclose_path(gx_path * ppath, int count)
236*593dc095SDavid du Colombier {
237*593dc095SDavid du Colombier subpath *psub;
238*593dc095SDavid du Colombier
239*593dc095SDavid du Colombier for (psub = ppath->first_subpath; count != 0;
240*593dc095SDavid du Colombier psub = (subpath *) psub->last->next
241*593dc095SDavid du Colombier )
242*593dc095SDavid du Colombier if (psub->last == (segment *) & psub->closer) {
243*593dc095SDavid du Colombier segment *prev = psub->closer.prev, *next = psub->closer.next;
244*593dc095SDavid du Colombier
245*593dc095SDavid du Colombier prev->next = next;
246*593dc095SDavid du Colombier if (next)
247*593dc095SDavid du Colombier next->prev = prev;
248*593dc095SDavid du Colombier psub->last = prev;
249*593dc095SDavid du Colombier count--;
250*593dc095SDavid du Colombier }
251*593dc095SDavid du Colombier }
252*593dc095SDavid du Colombier
2537dd7cddfSDavid du Colombier /*
2547dd7cddfSDavid du Colombier * Tweak the fill adjustment if necessary so that (nearly) empty
2557dd7cddfSDavid du Colombier * rectangles are guaranteed to produce some output. This is a hack
2567dd7cddfSDavid du Colombier * to work around a bug in the Microsoft Windows PostScript driver,
2577dd7cddfSDavid du Colombier * which draws thin lines by filling zero-width rectangles, and in
2587dd7cddfSDavid du Colombier * some other drivers that try to fill epsilon-width rectangles.
2597dd7cddfSDavid du Colombier */
2607dd7cddfSDavid du Colombier void
gx_adjust_if_empty(const gs_fixed_rect * pbox,gs_fixed_point * adjust)2617dd7cddfSDavid du Colombier gx_adjust_if_empty(const gs_fixed_rect * pbox, gs_fixed_point * adjust)
2627dd7cddfSDavid du Colombier {
2637dd7cddfSDavid du Colombier /*
2647dd7cddfSDavid du Colombier * For extremely large coordinates, the obvious subtractions could
2657dd7cddfSDavid du Colombier * overflow. We can work around this easily by noting that since
2667dd7cddfSDavid du Colombier * we know q.{x,y} >= p.{x,y}, the subtraction overflows iff the
2677dd7cddfSDavid du Colombier * result is negative.
2687dd7cddfSDavid du Colombier */
2697dd7cddfSDavid du Colombier const fixed
2707dd7cddfSDavid du Colombier dx = pbox->q.x - pbox->p.x, dy = pbox->q.y - pbox->p.y;
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier if (dx < fixed_half && dx > 0 && (dy >= int2fixed(2) || dy < 0)) {
2737dd7cddfSDavid du Colombier adjust->x = arith_rshift_1(fixed_1 + fixed_epsilon - dx);
2747dd7cddfSDavid du Colombier if_debug1('f', "[f]thin adjust_x=%g\n",
2757dd7cddfSDavid du Colombier fixed2float(adjust->x));
2767dd7cddfSDavid du Colombier } else if (dy < fixed_half && dy > 0 && (dx >= int2fixed(2) || dx < 0)) {
2777dd7cddfSDavid du Colombier adjust->y = arith_rshift_1(fixed_1 + fixed_epsilon - dy);
2787dd7cddfSDavid du Colombier if_debug1('f', "[f]thin adjust_y=%g\n",
2797dd7cddfSDavid du Colombier fixed2float(adjust->y));
2807dd7cddfSDavid du Colombier }
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier
2837dd7cddfSDavid du Colombier /*
2843ff48bf5SDavid du Colombier * The general fill path algorithm.
2857dd7cddfSDavid du Colombier */
2863ff48bf5SDavid du Colombier private int
gx_general_fill_path(gx_device * pdev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_device_color * pdevc,const gx_clip_path * pcpath)2873ff48bf5SDavid du Colombier gx_general_fill_path(gx_device * pdev, const gs_imager_state * pis,
2887dd7cddfSDavid du Colombier gx_path * ppath, const gx_fill_params * params,
2897dd7cddfSDavid du Colombier const gx_device_color * pdevc, const gx_clip_path * pcpath)
2907dd7cddfSDavid du Colombier {
2917dd7cddfSDavid du Colombier gs_fixed_point adjust;
2927dd7cddfSDavid du Colombier gs_logical_operation_t lop = pis->log_op;
293*593dc095SDavid du Colombier gs_fixed_rect ibox, bbox, sbox;
2947dd7cddfSDavid du Colombier gx_device_clip cdev;
2957dd7cddfSDavid du Colombier gx_device *dev = pdev;
2967dd7cddfSDavid du Colombier gx_device *save_dev = dev;
2977dd7cddfSDavid du Colombier gx_path ffpath;
2987dd7cddfSDavid du Colombier gx_path *pfpath;
2997dd7cddfSDavid du Colombier int code;
3007dd7cddfSDavid du Colombier int max_fill_band = dev->max_fill_band;
3013ff48bf5SDavid du Colombier #define NO_BAND_MASK ((fixed)(-1) << (sizeof(fixed) * 8 - 1))
302*593dc095SDavid du Colombier const bool is_character = params->adjust.x == -1; /* See gxistate.h */
3037dd7cddfSDavid du Colombier bool fill_by_trapezoids;
304*593dc095SDavid du Colombier bool pseudo_rasterization;
305*593dc095SDavid du Colombier bool big_path = ppath->subpath_count > 50;
306*593dc095SDavid du Colombier fill_options fo;
3077dd7cddfSDavid du Colombier line_list lst;
3087dd7cddfSDavid du Colombier
309*593dc095SDavid du Colombier *(const fill_options **)&lst.fo = &fo; /* break 'const'. */
3107dd7cddfSDavid du Colombier /*
3117dd7cddfSDavid du Colombier * Compute the bounding box before we flatten the path.
3127dd7cddfSDavid du Colombier * This can save a lot of time if the path has curves.
3137dd7cddfSDavid du Colombier * If the path is neither fully within nor fully outside
3147dd7cddfSDavid du Colombier * the quick-check boxes, we could recompute the bounding box
3157dd7cddfSDavid du Colombier * and make the checks again after flattening the path,
3167dd7cddfSDavid du Colombier * but right now we don't bother.
3177dd7cddfSDavid du Colombier */
3187dd7cddfSDavid du Colombier gx_path_bbox(ppath, &ibox);
319*593dc095SDavid du Colombier # define SMALL_CHARACTER 500
320*593dc095SDavid du Colombier pseudo_rasterization = (is_character &&
321*593dc095SDavid du Colombier !is_spotan_device(dev) &&
322*593dc095SDavid du Colombier ibox.q.y - ibox.p.y < SMALL_CHARACTER * fixed_scale &&
323*593dc095SDavid du Colombier ibox.q.x - ibox.p.x < SMALL_CHARACTER * fixed_scale);
324*593dc095SDavid du Colombier if (is_character)
325*593dc095SDavid du Colombier adjust.x = adjust.y = 0;
326*593dc095SDavid du Colombier else
327*593dc095SDavid du Colombier adjust = params->adjust;
328*593dc095SDavid du Colombier if (params->fill_zero_width && !pseudo_rasterization)
3297dd7cddfSDavid du Colombier gx_adjust_if_empty(&ibox, &adjust);
330*593dc095SDavid du Colombier lst.bbox_left = fixed2int(ibox.p.x - adjust.x - fixed_epsilon);
331*593dc095SDavid du Colombier lst.bbox_width = fixed2int(fixed_ceiling(ibox.q.x + adjust.x)) - lst.bbox_left;
332*593dc095SDavid du Colombier if (vd_enabled) {
333*593dc095SDavid du Colombier fixed x0 = int2fixed(fixed2int(ibox.p.x - adjust.x - fixed_epsilon));
334*593dc095SDavid du Colombier fixed x1 = int2fixed(fixed2int(ibox.q.x + adjust.x + fixed_scale - fixed_epsilon));
335*593dc095SDavid du Colombier fixed y0 = int2fixed(fixed2int(ibox.p.y - adjust.y - fixed_epsilon));
336*593dc095SDavid du Colombier fixed y1 = int2fixed(fixed2int(ibox.q.y + adjust.y + fixed_scale - fixed_epsilon)), k;
337*593dc095SDavid du Colombier
338*593dc095SDavid du Colombier for (k = x0; k <= x1; k += fixed_scale)
339*593dc095SDavid du Colombier vd_bar(k, y0, k, y1, 1, RGB(128, 128, 128));
340*593dc095SDavid du Colombier for (k = y0; k <= y1; k += fixed_scale)
341*593dc095SDavid du Colombier vd_bar(x0, k, x1, k, 1, RGB(128, 128, 128));
342*593dc095SDavid du Colombier }
3437dd7cddfSDavid du Colombier /* Check the bounding boxes. */
3447dd7cddfSDavid du Colombier if_debug6('f', "[f]adjust=%g,%g bbox=(%g,%g),(%g,%g)\n",
3453ff48bf5SDavid du Colombier fixed2float(adjust.x), fixed2float(adjust.y),
3467dd7cddfSDavid du Colombier fixed2float(ibox.p.x), fixed2float(ibox.p.y),
3477dd7cddfSDavid du Colombier fixed2float(ibox.q.x), fixed2float(ibox.q.y));
3487dd7cddfSDavid du Colombier if (pcpath)
3497dd7cddfSDavid du Colombier gx_cpath_inner_box(pcpath, &bbox);
3507dd7cddfSDavid du Colombier else
3517dd7cddfSDavid du Colombier (*dev_proc(dev, get_clipping_box)) (dev, &bbox);
3523ff48bf5SDavid du Colombier if (!rect_within(ibox, bbox)) {
3533ff48bf5SDavid du Colombier /*
3547dd7cddfSDavid du Colombier * Intersect the path box and the clip bounding box.
3557dd7cddfSDavid du Colombier * If the intersection is empty, this fill is a no-op.
3567dd7cddfSDavid du Colombier */
3577dd7cddfSDavid du Colombier if (pcpath)
3587dd7cddfSDavid du Colombier gx_cpath_outer_box(pcpath, &bbox);
3597dd7cddfSDavid du Colombier if_debug4('f', " outer_box=(%g,%g),(%g,%g)\n",
3607dd7cddfSDavid du Colombier fixed2float(bbox.p.x), fixed2float(bbox.p.y),
3617dd7cddfSDavid du Colombier fixed2float(bbox.q.x), fixed2float(bbox.q.y));
3627dd7cddfSDavid du Colombier rect_intersect(ibox, bbox);
3633ff48bf5SDavid du Colombier if (ibox.p.x - adjust.x >= ibox.q.x + adjust.x ||
3643ff48bf5SDavid du Colombier ibox.p.y - adjust.y >= ibox.q.y + adjust.y
3657dd7cddfSDavid du Colombier ) { /* Intersection of boxes is empty! */
3667dd7cddfSDavid du Colombier return 0;
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier /*
3697dd7cddfSDavid du Colombier * The path is neither entirely inside the inner clip box
3707dd7cddfSDavid du Colombier * nor entirely outside the outer clip box.
3717dd7cddfSDavid du Colombier * If we had to flatten the path, this is where we would
3727dd7cddfSDavid du Colombier * recompute its bbox and make the tests again,
3737dd7cddfSDavid du Colombier * but we don't bother right now.
3747dd7cddfSDavid du Colombier *
3757dd7cddfSDavid du Colombier * If there is a clipping path, set up a clipping device.
3767dd7cddfSDavid du Colombier */
3777dd7cddfSDavid du Colombier if (pcpath) {
3787dd7cddfSDavid du Colombier dev = (gx_device *) & cdev;
3797dd7cddfSDavid du Colombier gx_make_clip_device(&cdev, gx_cpath_list(pcpath));
3807dd7cddfSDavid du Colombier cdev.target = save_dev;
3817dd7cddfSDavid du Colombier cdev.max_fill_band = save_dev->max_fill_band;
3827dd7cddfSDavid du Colombier (*dev_proc(dev, open_device)) (dev);
3837dd7cddfSDavid du Colombier }
3847dd7cddfSDavid du Colombier }
3857dd7cddfSDavid du Colombier /*
3867dd7cddfSDavid du Colombier * Compute the proper adjustment values.
3877dd7cddfSDavid du Colombier * To get the effect of the any-part-of-pixel rule,
3887dd7cddfSDavid du Colombier * we may have to tweak them slightly.
3897dd7cddfSDavid du Colombier * NOTE: We changed the adjust_right/above value from 0.5+epsilon
3907dd7cddfSDavid du Colombier * to 0.5 in release 5.01; even though this does the right thing
3917dd7cddfSDavid du Colombier * in every case we could imagine, we aren't confident that it's
3927dd7cddfSDavid du Colombier * correct. (The old values were definitely incorrect, since they
3937dd7cddfSDavid du Colombier * caused 1-pixel-wide/high objects to color 2 pixels even if
3947dd7cddfSDavid du Colombier * they fell exactly on pixel boundaries.)
3957dd7cddfSDavid du Colombier */
3967dd7cddfSDavid du Colombier if (adjust.x == fixed_half)
397*593dc095SDavid du Colombier fo.adjust_left = fixed_half - fixed_epsilon,
398*593dc095SDavid du Colombier fo.adjust_right = fixed_half /* + fixed_epsilon */ ; /* see above */
3997dd7cddfSDavid du Colombier else
400*593dc095SDavid du Colombier fo.adjust_left = fo.adjust_right = adjust.x;
4017dd7cddfSDavid du Colombier if (adjust.y == fixed_half)
402*593dc095SDavid du Colombier fo.adjust_below = fixed_half - fixed_epsilon,
403*593dc095SDavid du Colombier fo.adjust_above = fixed_half /* + fixed_epsilon */ ; /* see above */
4047dd7cddfSDavid du Colombier else
405*593dc095SDavid du Colombier fo.adjust_below = fo.adjust_above = adjust.y;
4067dd7cddfSDavid du Colombier /* Initialize the active line list. */
4077dd7cddfSDavid du Colombier init_line_list(&lst, ppath->memory);
408*593dc095SDavid du Colombier sbox.p.x = ibox.p.x - adjust.x;
409*593dc095SDavid du Colombier sbox.p.y = ibox.p.y - adjust.y;
410*593dc095SDavid du Colombier sbox.q.x = ibox.q.x + adjust.x;
411*593dc095SDavid du Colombier sbox.q.y = ibox.q.y + adjust.y;
412*593dc095SDavid du Colombier fo.pseudo_rasterization = pseudo_rasterization;
413*593dc095SDavid du Colombier fo.pdevc = pdevc;
414*593dc095SDavid du Colombier fo.lop = lop;
415*593dc095SDavid du Colombier fo.fixed_flat = float2fixed(params->flatness);
416*593dc095SDavid du Colombier fo.ymin = ibox.p.y;
417*593dc095SDavid du Colombier fo.ymax = ibox.q.y;
418*593dc095SDavid du Colombier fo.dev = dev;
419*593dc095SDavid du Colombier fo.lop = lop;
420*593dc095SDavid du Colombier fo.pbox = &sbox;
421*593dc095SDavid du Colombier fo.rule = params->rule;
422*593dc095SDavid du Colombier fo.is_spotan = is_spotan_device(dev);
423*593dc095SDavid du Colombier fo.fill_direct = color_writes_pure(pdevc, lop);
424*593dc095SDavid du Colombier fo.fill_rect = (fo.fill_direct ? dev_proc(dev, fill_rectangle) : NULL);
425*593dc095SDavid du Colombier fo.fill_trap = dev_proc(dev, fill_trapezoid);
426*593dc095SDavid du Colombier
4277dd7cddfSDavid du Colombier /*
4287dd7cddfSDavid du Colombier * We have a choice of two different filling algorithms:
4297dd7cddfSDavid du Colombier * scan-line-based and trapezoid-based. They compare as follows:
4307dd7cddfSDavid du Colombier *
4317dd7cddfSDavid du Colombier * Scan Trap
4327dd7cddfSDavid du Colombier * ---- ----
4337dd7cddfSDavid du Colombier * skip +draw 0-height horizontal lines
4347dd7cddfSDavid du Colombier * slow +fast rectangles
4357dd7cddfSDavid du Colombier * +fast slow curves
4367dd7cddfSDavid du Colombier * +yes no write pixels at most once when adjust != 0
4377dd7cddfSDavid du Colombier *
4383ff48bf5SDavid du Colombier * Normally we use the scan line algorithm for characters, where curve
4393ff48bf5SDavid du Colombier * speed is important, and for non-idempotent RasterOps, where double
4403ff48bf5SDavid du Colombier * pixel writing must be avoided, and the trapezoid algorithm otherwise.
4413ff48bf5SDavid du Colombier * However, we always use the trapezoid algorithm for rectangles.
4427dd7cddfSDavid du Colombier */
4437dd7cddfSDavid du Colombier fill_by_trapezoids =
444*593dc095SDavid du Colombier (pseudo_rasterization || !gx_path_has_curves(ppath) ||
445*593dc095SDavid du Colombier params->flatness >= 1.0 || fo.is_spotan);
446*593dc095SDavid du Colombier if (fill_by_trapezoids && !fo.is_spotan && !lop_is_idempotent(lop)) {
4477dd7cddfSDavid du Colombier gs_fixed_rect rbox;
4487dd7cddfSDavid du Colombier
4497dd7cddfSDavid du Colombier if (gx_path_is_rectangular(ppath, &rbox)) {
450*593dc095SDavid du Colombier int x0 = fixed2int_pixround(rbox.p.x - fo.adjust_left);
451*593dc095SDavid du Colombier int y0 = fixed2int_pixround(rbox.p.y - fo.adjust_below);
452*593dc095SDavid du Colombier int x1 = fixed2int_pixround(rbox.q.x + fo.adjust_right);
453*593dc095SDavid du Colombier int y1 = fixed2int_pixround(rbox.q.y + fo.adjust_above);
4547dd7cddfSDavid du Colombier
4557dd7cddfSDavid du Colombier return gx_fill_rectangle_device_rop(x0, y0, x1 - x0, y1 - y0,
4567dd7cddfSDavid du Colombier pdevc, dev, lop);
4577dd7cddfSDavid du Colombier }
458*593dc095SDavid du Colombier if (fo.adjust_left | fo.adjust_right | fo.adjust_below | fo.adjust_above)
459*593dc095SDavid du Colombier fill_by_trapezoids = false; /* avoid double writing pixels */
4607dd7cddfSDavid du Colombier }
4617dd7cddfSDavid du Colombier gx_path_init_local(&ffpath, ppath->memory);
462*593dc095SDavid du Colombier if (!big_path && !gx_path_has_curves(ppath)) /* don't need to flatten */
4637dd7cddfSDavid du Colombier pfpath = ppath;
464*593dc095SDavid du Colombier else if (is_spotan_device(dev))
465*593dc095SDavid du Colombier pfpath = ppath;
466*593dc095SDavid du Colombier else if (!big_path && gx_path__check_curves(ppath, pco_small_curves, fo.fixed_flat))
4673ff48bf5SDavid du Colombier pfpath = ppath;
4683ff48bf5SDavid du Colombier else {
469*593dc095SDavid du Colombier code = gx_path_copy_reducing(ppath, &ffpath, fo.fixed_flat, NULL,
470*593dc095SDavid du Colombier pco_small_curves);
4713ff48bf5SDavid du Colombier if (code < 0)
4723ff48bf5SDavid du Colombier return code;
4733ff48bf5SDavid du Colombier pfpath = &ffpath;
474*593dc095SDavid du Colombier if (big_path) {
475*593dc095SDavid du Colombier code = gx_path_merge_contacting_contours(pfpath);
476*593dc095SDavid du Colombier if (code < 0)
477*593dc095SDavid du Colombier return code;
4783ff48bf5SDavid du Colombier }
479*593dc095SDavid du Colombier }
480*593dc095SDavid du Colombier fo.fill_by_trapezoids = fill_by_trapezoids;
481*593dc095SDavid du Colombier if ((code = add_y_list(pfpath, &lst)) < 0)
4827dd7cddfSDavid du Colombier goto nope;
4837dd7cddfSDavid du Colombier {
4843ff48bf5SDavid du Colombier FILL_LOOP_PROC((*fill_loop));
4857dd7cddfSDavid du Colombier
4867dd7cddfSDavid du Colombier /* Some short-sighted compilers won't allow a conditional here.... */
4877dd7cddfSDavid du Colombier if (fill_by_trapezoids)
488*593dc095SDavid du Colombier fill_loop = spot_into_trapezoids;
4897dd7cddfSDavid du Colombier else
490*593dc095SDavid du Colombier fill_loop = spot_into_scan_lines;
491*593dc095SDavid du Colombier if (lst.bbox_width > MAX_LOCAL_SECTION && fo.pseudo_rasterization) {
492*593dc095SDavid du Colombier /*
493*593dc095SDavid du Colombier * Note that execution pass here only for character size
494*593dc095SDavid du Colombier * grater that MAX_LOCAL_SECTION and lesser than
495*593dc095SDavid du Colombier * SMALL_CHARACTER. Therefore with !arch_small_memory
496*593dc095SDavid du Colombier * the dynamic allocation only happens for characters
497*593dc095SDavid du Colombier * wider than 100 pixels.
498*593dc095SDavid du Colombier */
499*593dc095SDavid du Colombier lst.margin_set0.sect = (section *)gs_alloc_struct_array(pdev->memory, lst.bbox_width * 2,
500*593dc095SDavid du Colombier section, &st_section, "section");
501*593dc095SDavid du Colombier if (lst.margin_set0.sect == 0)
502*593dc095SDavid du Colombier return_error(gs_error_VMerror);
503*593dc095SDavid du Colombier lst.margin_set1.sect = lst.margin_set0.sect + lst.bbox_width;
504*593dc095SDavid du Colombier }
505*593dc095SDavid du Colombier if (fo.pseudo_rasterization) {
506*593dc095SDavid du Colombier init_section(lst.margin_set0.sect, 0, lst.bbox_width);
507*593dc095SDavid du Colombier init_section(lst.margin_set1.sect, 0, lst.bbox_width);
508*593dc095SDavid du Colombier }
5097dd7cddfSDavid du Colombier code = (*fill_loop)
510*593dc095SDavid du Colombier (&lst, (max_fill_band == 0 ? NO_BAND_MASK : int2fixed(-max_fill_band)));
511*593dc095SDavid du Colombier if (lst.margin_set0.sect != lst.local_section0 &&
512*593dc095SDavid du Colombier lst.margin_set0.sect != lst.local_section1)
513*593dc095SDavid du Colombier gs_free_object(pdev->memory, min(lst.margin_set0.sect, lst.margin_set1.sect), "section");
5147dd7cddfSDavid du Colombier }
5157dd7cddfSDavid du Colombier nope:if (lst.close_count != 0)
5167dd7cddfSDavid du Colombier unclose_path(pfpath, lst.close_count);
5177dd7cddfSDavid du Colombier free_line_list(&lst);
5187dd7cddfSDavid du Colombier if (pfpath != ppath) /* had to flatten */
519*593dc095SDavid du Colombier gx_path_free(pfpath, "gx_general_fill_path");
5207dd7cddfSDavid du Colombier #ifdef DEBUG
5217dd7cddfSDavid du Colombier if (gs_debug_c('f')) {
5227dd7cddfSDavid du Colombier dlputs("[f] # alloc up down horiz step slowx iter find band bstep bfill\n");
5237dd7cddfSDavid du Colombier dlprintf5(" %5ld %5ld %5ld %5ld %5ld",
5247dd7cddfSDavid du Colombier stats_fill.fill, stats_fill.fill_alloc,
5257dd7cddfSDavid du Colombier stats_fill.y_up, stats_fill.y_down,
5267dd7cddfSDavid du Colombier stats_fill.horiz);
5277dd7cddfSDavid du Colombier dlprintf4(" %5ld %5ld %5ld %5ld",
5287dd7cddfSDavid du Colombier stats_fill.x_step, stats_fill.slow_x,
5297dd7cddfSDavid du Colombier stats_fill.iter, stats_fill.find_y);
5307dd7cddfSDavid du Colombier dlprintf3(" %5ld %5ld %5ld\n",
5317dd7cddfSDavid du Colombier stats_fill.band, stats_fill.band_step,
5327dd7cddfSDavid du Colombier stats_fill.band_fill);
5337dd7cddfSDavid du Colombier dlputs("[f] afill slant shall sfill mqcrs order slowo\n");
5347dd7cddfSDavid du Colombier dlprintf7(" %5ld %5ld %5ld %5ld %5ld %5ld %5ld\n",
5357dd7cddfSDavid du Colombier stats_fill.afill, stats_fill.slant,
5367dd7cddfSDavid du Colombier stats_fill.slant_shallow, stats_fill.sfill,
5377dd7cddfSDavid du Colombier stats_fill.mq_cross, stats_fill.order,
5387dd7cddfSDavid du Colombier stats_fill.slow_order);
5397dd7cddfSDavid du Colombier }
5407dd7cddfSDavid du Colombier #endif
5417dd7cddfSDavid du Colombier return code;
5427dd7cddfSDavid du Colombier }
5437dd7cddfSDavid du Colombier
5443ff48bf5SDavid du Colombier /*
5453ff48bf5SDavid du Colombier * Fill a path. This is the default implementation of the driver
5463ff48bf5SDavid du Colombier * fill_path procedure.
5473ff48bf5SDavid du Colombier */
5483ff48bf5SDavid du Colombier int
gx_default_fill_path(gx_device * pdev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_device_color * pdevc,const gx_clip_path * pcpath)5493ff48bf5SDavid du Colombier gx_default_fill_path(gx_device * pdev, const gs_imager_state * pis,
5503ff48bf5SDavid du Colombier gx_path * ppath, const gx_fill_params * params,
5513ff48bf5SDavid du Colombier const gx_device_color * pdevc, const gx_clip_path * pcpath)
5523ff48bf5SDavid du Colombier {
553*593dc095SDavid du Colombier int code;
554*593dc095SDavid du Colombier
5553ff48bf5SDavid du Colombier if (gx_dc_is_pattern2_color(pdevc)) {
5563ff48bf5SDavid du Colombier /* Optimization for shading fill :
5573ff48bf5SDavid du Colombier The general path filling algorithm subdivides fill region with
5583ff48bf5SDavid du Colombier trapezoid or rectangle subregions and then paints each subregion
5593ff48bf5SDavid du Colombier with given color. If the color is shading, each subregion to be
5603ff48bf5SDavid du Colombier subdivided into areas of constant color. But with radial
5613ff48bf5SDavid du Colombier shading each area is a high order polygon, being
5623ff48bf5SDavid du Colombier subdivided into smaller subregions, so as total number of
5633ff48bf5SDavid du Colombier subregions grows huge. Faster processing is done here by changing
5643ff48bf5SDavid du Colombier the order of subdivision cycles : we first subdivide the shading into
5653ff48bf5SDavid du Colombier areas of constant color, then apply the general path filling algorithm
5663ff48bf5SDavid du Colombier (i.e. subdivide each area into trapezoids or rectangles), using the
5673ff48bf5SDavid du Colombier filling path as clip mask.
5683ff48bf5SDavid du Colombier */
5693ff48bf5SDavid du Colombier
5703ff48bf5SDavid du Colombier gx_clip_path cpath_intersection;
5713ff48bf5SDavid du Colombier gx_path path_intersection;
5723ff48bf5SDavid du Colombier
5733ff48bf5SDavid du Colombier /* Shading fill algorithm uses "current path" parameter of the general
5743ff48bf5SDavid du Colombier path filling algorithm as boundary of constant color area,
5753ff48bf5SDavid du Colombier so we need to intersect the filling path with the clip path now,
5763ff48bf5SDavid du Colombier reducing the number of pathes passed to it :
5773ff48bf5SDavid du Colombier */
5783ff48bf5SDavid du Colombier gx_path_init_local(&path_intersection, pdev->memory);
5793ff48bf5SDavid du Colombier gx_cpath_init_local_shared(&cpath_intersection, pcpath, pdev->memory);
5803ff48bf5SDavid du Colombier if ((code = gx_cpath_intersect(&cpath_intersection, ppath, params->rule, (gs_imager_state *)pis)) >= 0)
5813ff48bf5SDavid du Colombier code = gx_cpath_to_path(&cpath_intersection, &path_intersection);
5823ff48bf5SDavid du Colombier
5833ff48bf5SDavid du Colombier /* Do fill : */
5843ff48bf5SDavid du Colombier if (code >= 0)
585*593dc095SDavid du Colombier code = gx_dc_pattern2_fill_path(pdevc, &path_intersection, NULL, pdev);
5863ff48bf5SDavid du Colombier
5873ff48bf5SDavid du Colombier /* Destruct local data and return :*/
5883ff48bf5SDavid du Colombier gx_path_free(&path_intersection, "shading_fill_path_intersection");
5893ff48bf5SDavid du Colombier gx_cpath_free(&cpath_intersection, "shading_fill_cpath_intersection");
590*593dc095SDavid du Colombier } else {
591*593dc095SDavid du Colombier bool got_dc = false;
592*593dc095SDavid du Colombier vd_save;
593*593dc095SDavid du Colombier if (vd_allowed('F') || vd_allowed('f')) {
594*593dc095SDavid du Colombier if (!vd_enabled) {
595*593dc095SDavid du Colombier vd_get_dc( (params->adjust.x | params->adjust.y) ? 'F' : 'f');
596*593dc095SDavid du Colombier got_dc = vd_enabled;
597*593dc095SDavid du Colombier }
598*593dc095SDavid du Colombier if (vd_enabled) {
599*593dc095SDavid du Colombier vd_set_shift(0, 100);
600*593dc095SDavid du Colombier vd_set_scale(VD_SCALE);
601*593dc095SDavid du Colombier vd_set_origin(0, 0);
602*593dc095SDavid du Colombier vd_erase(RGB(192, 192, 192));
603*593dc095SDavid du Colombier }
604*593dc095SDavid du Colombier } else
605*593dc095SDavid du Colombier vd_disable;
606*593dc095SDavid du Colombier code = gx_general_fill_path(pdev, pis, ppath, params, pdevc, pcpath);
607*593dc095SDavid du Colombier if (got_dc)
608*593dc095SDavid du Colombier vd_release_dc;
609*593dc095SDavid du Colombier vd_restore;
610*593dc095SDavid du Colombier }
6113ff48bf5SDavid du Colombier return code;
6123ff48bf5SDavid du Colombier }
6137dd7cddfSDavid du Colombier
6147dd7cddfSDavid du Colombier /* Free the line list. */
6157dd7cddfSDavid du Colombier private void
free_line_list(line_list * ll)616*593dc095SDavid du Colombier free_line_list(line_list *ll)
6177dd7cddfSDavid du Colombier {
6183ff48bf5SDavid du Colombier /* Free any individually allocated active_lines. */
6197dd7cddfSDavid du Colombier gs_memory_t *mem = ll->memory;
6207dd7cddfSDavid du Colombier active_line *alp;
6217dd7cddfSDavid du Colombier
6227dd7cddfSDavid du Colombier while ((alp = ll->active_area) != 0) {
6237dd7cddfSDavid du Colombier active_line *next = alp->alloc_next;
6247dd7cddfSDavid du Colombier
6257dd7cddfSDavid du Colombier gs_free_object(mem, alp, "active line");
6267dd7cddfSDavid du Colombier ll->active_area = next;
6277dd7cddfSDavid du Colombier }
628*593dc095SDavid du Colombier free_all_margins(ll);
6297dd7cddfSDavid du Colombier }
6307dd7cddfSDavid du Colombier
631*593dc095SDavid du Colombier private inline active_line *
make_al(line_list * ll)632*593dc095SDavid du Colombier make_al(line_list *ll)
6337dd7cddfSDavid du Colombier {
6343ff48bf5SDavid du Colombier active_line *alp = ll->next_active;
6357dd7cddfSDavid du Colombier
6367dd7cddfSDavid du Colombier if (alp == ll->limit) { /* Allocate separately */
6377dd7cddfSDavid du Colombier alp = gs_alloc_struct(ll->memory, active_line,
6387dd7cddfSDavid du Colombier &st_active_line, "active line");
6397dd7cddfSDavid du Colombier if (alp == 0)
640*593dc095SDavid du Colombier return NULL;
6417dd7cddfSDavid du Colombier alp->alloc_next = ll->active_area;
6427dd7cddfSDavid du Colombier ll->active_area = alp;
6437dd7cddfSDavid du Colombier INCR(fill_alloc);
6447dd7cddfSDavid du Colombier } else
6457dd7cddfSDavid du Colombier ll->next_active++;
646*593dc095SDavid du Colombier return alp;
6477dd7cddfSDavid du Colombier }
648*593dc095SDavid du Colombier
6497dd7cddfSDavid du Colombier /* Insert the new line in the Y ordering */
650*593dc095SDavid du Colombier private void
insert_y_line(line_list * ll,active_line * alp)651*593dc095SDavid du Colombier insert_y_line(line_list *ll, active_line *alp)
6527dd7cddfSDavid du Colombier {
6533ff48bf5SDavid du Colombier active_line *yp = ll->y_line;
6543ff48bf5SDavid du Colombier active_line *nyp;
655*593dc095SDavid du Colombier fixed y_start = alp->start.y;
6567dd7cddfSDavid du Colombier
657*593dc095SDavid du Colombier vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 0, RGB(255, 0, 0));
6587dd7cddfSDavid du Colombier if (yp == 0) {
6597dd7cddfSDavid du Colombier alp->next = alp->prev = 0;
6607dd7cddfSDavid du Colombier ll->y_list = alp;
6617dd7cddfSDavid du Colombier } else if (y_start >= yp->start.y) { /* Insert the new line after y_line */
6627dd7cddfSDavid du Colombier while (INCR_EXPR(y_up),
6637dd7cddfSDavid du Colombier ((nyp = yp->next) != NULL &&
6647dd7cddfSDavid du Colombier y_start > nyp->start.y)
6657dd7cddfSDavid du Colombier )
6667dd7cddfSDavid du Colombier yp = nyp;
6677dd7cddfSDavid du Colombier alp->next = nyp;
6687dd7cddfSDavid du Colombier alp->prev = yp;
6697dd7cddfSDavid du Colombier yp->next = alp;
6707dd7cddfSDavid du Colombier if (nyp)
6717dd7cddfSDavid du Colombier nyp->prev = alp;
6727dd7cddfSDavid du Colombier } else { /* Insert the new line before y_line */
6737dd7cddfSDavid du Colombier while (INCR_EXPR(y_down),
6747dd7cddfSDavid du Colombier ((nyp = yp->prev) != NULL &&
6757dd7cddfSDavid du Colombier y_start < nyp->start.y)
6767dd7cddfSDavid du Colombier )
6777dd7cddfSDavid du Colombier yp = nyp;
6787dd7cddfSDavid du Colombier alp->prev = nyp;
6797dd7cddfSDavid du Colombier alp->next = yp;
6807dd7cddfSDavid du Colombier yp->prev = alp;
6817dd7cddfSDavid du Colombier if (nyp)
6827dd7cddfSDavid du Colombier nyp->next = alp;
6837dd7cddfSDavid du Colombier else
6847dd7cddfSDavid du Colombier ll->y_list = alp;
6857dd7cddfSDavid du Colombier }
6867dd7cddfSDavid du Colombier ll->y_line = alp;
687*593dc095SDavid du Colombier vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 1, RGB(0, 255, 0));
6887dd7cddfSDavid du Colombier print_al("add ", alp);
689*593dc095SDavid du Colombier }
690*593dc095SDavid du Colombier
691*593dc095SDavid du Colombier typedef struct contour_cursor_s {
692*593dc095SDavid du Colombier segment *prev, *pseg, *pfirst, *plast;
693*593dc095SDavid du Colombier gx_flattened_iterator *fi;
694*593dc095SDavid du Colombier bool more_flattened;
695*593dc095SDavid du Colombier bool first_flattened;
696*593dc095SDavid du Colombier int dir;
697*593dc095SDavid du Colombier bool monotonic_y;
698*593dc095SDavid du Colombier bool monotonic_x;
699*593dc095SDavid du Colombier bool crossing;
700*593dc095SDavid du Colombier } contour_cursor;
701*593dc095SDavid du Colombier
702*593dc095SDavid du Colombier private inline int
compute_dir(const fill_options * fo,fixed y0,fixed y1)703*593dc095SDavid du Colombier compute_dir(const fill_options *fo, fixed y0, fixed y1)
704*593dc095SDavid du Colombier {
705*593dc095SDavid du Colombier if (max(y0, y1) < fo->ymin)
706*593dc095SDavid du Colombier return 2;
707*593dc095SDavid du Colombier if (min(y0, y1) > fo->ymax)
708*593dc095SDavid du Colombier return 2;
709*593dc095SDavid du Colombier return (y0 < y1 ? DIR_UP :
710*593dc095SDavid du Colombier y0 > y1 ? DIR_DOWN : DIR_HORIZONTAL);
711*593dc095SDavid du Colombier }
712*593dc095SDavid du Colombier
713*593dc095SDavid du Colombier private inline int
add_y_curve_part(line_list * ll,segment * s0,segment * s1,int dir,gx_flattened_iterator * fi,bool more1,bool step_back,bool monotonic_x)714*593dc095SDavid du Colombier add_y_curve_part(line_list *ll, segment *s0, segment *s1, int dir,
715*593dc095SDavid du Colombier gx_flattened_iterator *fi, bool more1, bool step_back, bool monotonic_x)
716*593dc095SDavid du Colombier {
717*593dc095SDavid du Colombier active_line *alp = make_al(ll);
718*593dc095SDavid du Colombier
719*593dc095SDavid du Colombier if (alp == NULL)
720*593dc095SDavid du Colombier return_error(gs_error_VMerror);
721*593dc095SDavid du Colombier alp->pseg = (dir == DIR_UP ? s1 : s0);
722*593dc095SDavid du Colombier alp->direction = dir;
723*593dc095SDavid du Colombier alp->fi = *fi;
724*593dc095SDavid du Colombier alp->more_flattened = more1;
725*593dc095SDavid du Colombier if (dir != DIR_UP && more1)
726*593dc095SDavid du Colombier gx_flattened_iterator__switch_to_backscan(&alp->fi, more1);
727*593dc095SDavid du Colombier if (step_back) {
728*593dc095SDavid du Colombier do {
729*593dc095SDavid du Colombier alp->more_flattened = gx_flattened_iterator__prev(&alp->fi);
730*593dc095SDavid du Colombier if (compute_dir(ll->fo, alp->fi.ly0, alp->fi.ly1) != 2)
731*593dc095SDavid du Colombier break;
732*593dc095SDavid du Colombier } while (alp->more_flattened);
733*593dc095SDavid du Colombier }
734*593dc095SDavid du Colombier step_al(alp, false);
735*593dc095SDavid du Colombier alp->monotonic_y = false;
736*593dc095SDavid du Colombier alp->monotonic_x = monotonic_x;
737*593dc095SDavid du Colombier insert_y_line(ll, alp);
7387dd7cddfSDavid du Colombier return 0;
7397dd7cddfSDavid du Colombier }
7407dd7cddfSDavid du Colombier
741*593dc095SDavid du Colombier private inline int
add_y_line(const segment * prev_lp,const segment * lp,int dir,line_list * ll)742*593dc095SDavid du Colombier add_y_line(const segment * prev_lp, const segment * lp, int dir, line_list *ll)
743*593dc095SDavid du Colombier {
744*593dc095SDavid du Colombier return add_y_line_aux(prev_lp, lp, &lp->pt, &prev_lp->pt, dir, ll);
745*593dc095SDavid du Colombier }
746*593dc095SDavid du Colombier
747*593dc095SDavid du Colombier private inline int
start_al_pair(line_list * ll,contour_cursor * q,contour_cursor * p)748*593dc095SDavid du Colombier start_al_pair(line_list *ll, contour_cursor *q, contour_cursor *p)
749*593dc095SDavid du Colombier {
750*593dc095SDavid du Colombier int code;
751*593dc095SDavid du Colombier
752*593dc095SDavid du Colombier if (q->monotonic_y)
753*593dc095SDavid du Colombier code = add_y_line(q->prev, q->pseg, DIR_DOWN, ll);
754*593dc095SDavid du Colombier else
755*593dc095SDavid du Colombier code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, q->fi,
756*593dc095SDavid du Colombier !q->first_flattened, false, q->monotonic_x);
757*593dc095SDavid du Colombier if (code < 0)
758*593dc095SDavid du Colombier return code;
759*593dc095SDavid du Colombier if (p->monotonic_y)
760*593dc095SDavid du Colombier code = add_y_line(p->prev, p->pseg, DIR_UP, ll);
761*593dc095SDavid du Colombier else
762*593dc095SDavid du Colombier code = add_y_curve_part(ll, p->prev, p->pseg, DIR_UP, p->fi,
763*593dc095SDavid du Colombier p->more_flattened, false, p->monotonic_x);
764*593dc095SDavid du Colombier return code;
765*593dc095SDavid du Colombier }
766*593dc095SDavid du Colombier
767*593dc095SDavid du Colombier /* Start active lines from a minimum of a possibly non-monotonic curve. */
768*593dc095SDavid du Colombier private int
start_al_pair_from_min(line_list * ll,contour_cursor * q)769*593dc095SDavid du Colombier start_al_pair_from_min(line_list *ll, contour_cursor *q)
770*593dc095SDavid du Colombier {
771*593dc095SDavid du Colombier int dir, code;
772*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
773*593dc095SDavid du Colombier
774*593dc095SDavid du Colombier /* q stands at the first segment, which isn't last. */
775*593dc095SDavid du Colombier do {
776*593dc095SDavid du Colombier q->more_flattened = gx_flattened_iterator__next(q->fi);
777*593dc095SDavid du Colombier dir = compute_dir(fo, q->fi->ly0, q->fi->ly1);
778*593dc095SDavid du Colombier if (q->fi->ly0 > fo->ymax && ll->y_break > q->fi->y0)
779*593dc095SDavid du Colombier ll->y_break = q->fi->ly0;
780*593dc095SDavid du Colombier if (q->fi->ly1 > fo->ymax && ll->y_break > q->fi->ly1)
781*593dc095SDavid du Colombier ll->y_break = q->fi->ly1;
782*593dc095SDavid du Colombier if (dir == DIR_UP && ll->main_dir == DIR_DOWN && q->fi->ly0 >= fo->ymin) {
783*593dc095SDavid du Colombier code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, q->fi,
784*593dc095SDavid du Colombier true, true, q->monotonic_x);
785*593dc095SDavid du Colombier if (code < 0)
786*593dc095SDavid du Colombier return code;
787*593dc095SDavid du Colombier code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, q->fi,
788*593dc095SDavid du Colombier q->more_flattened, false, q->monotonic_x);
789*593dc095SDavid du Colombier if (code < 0)
790*593dc095SDavid du Colombier return code;
791*593dc095SDavid du Colombier } else if (q->fi->ly0 < fo->ymin && q->fi->ly1 >= fo->ymin) {
792*593dc095SDavid du Colombier code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, q->fi,
793*593dc095SDavid du Colombier q->more_flattened, false, q->monotonic_x);
794*593dc095SDavid du Colombier if (code < 0)
795*593dc095SDavid du Colombier return code;
796*593dc095SDavid du Colombier } else if (q->fi->ly0 >= fo->ymin && q->fi->ly1 < fo->ymin) {
797*593dc095SDavid du Colombier code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, q->fi,
798*593dc095SDavid du Colombier true, false, q->monotonic_x);
799*593dc095SDavid du Colombier if (code < 0)
800*593dc095SDavid du Colombier return code;
801*593dc095SDavid du Colombier }
802*593dc095SDavid du Colombier q->first_flattened = false;
803*593dc095SDavid du Colombier q->dir = dir;
804*593dc095SDavid du Colombier ll->main_dir = (dir == DIR_DOWN ? DIR_DOWN :
805*593dc095SDavid du Colombier dir == DIR_UP ? DIR_UP : ll->main_dir);
806*593dc095SDavid du Colombier if (!q->more_flattened)
807*593dc095SDavid du Colombier break;
808*593dc095SDavid du Colombier } while(q->more_flattened);
809*593dc095SDavid du Colombier /* q stands at the last segment. */
810*593dc095SDavid du Colombier return 0;
811*593dc095SDavid du Colombier /* note : it doesn't depend on the number of curve minimums,
812*593dc095SDavid du Colombier which may vary due to arithmetic errors. */
813*593dc095SDavid du Colombier }
814*593dc095SDavid du Colombier
815*593dc095SDavid du Colombier private inline void
init_contour_cursor(line_list * ll,contour_cursor * q)816*593dc095SDavid du Colombier init_contour_cursor(line_list *ll, contour_cursor *q)
817*593dc095SDavid du Colombier {
818*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
819*593dc095SDavid du Colombier
820*593dc095SDavid du Colombier if (q->pseg->type == s_curve) {
821*593dc095SDavid du Colombier curve_segment *s = (curve_segment *)q->pseg;
822*593dc095SDavid du Colombier fixed ymin = min(min(q->prev->pt.y, s->p1.y), min(s->p2.y, s->pt.y));
823*593dc095SDavid du Colombier fixed ymax = max(max(q->prev->pt.y, s->p1.y), max(s->p2.y, s->pt.y));
824*593dc095SDavid du Colombier bool in_band = ymin <= fo->ymax && ymax >= fo->ymin;
825*593dc095SDavid du Colombier q->crossing = ymin < fo->ymin && ymax >= fo->ymin;
826*593dc095SDavid du Colombier q->monotonic_y = !in_band ||
827*593dc095SDavid du Colombier (!q->crossing &&
828*593dc095SDavid du Colombier ((q->prev->pt.y <= s->p1.y && s->p1.y <= s->p2.y && s->p2.y <= s->pt.y) ||
829*593dc095SDavid du Colombier (q->prev->pt.y >= s->p1.y && s->p1.y >= s->p2.y && s->p2.y >= s->pt.y)));
830*593dc095SDavid du Colombier q->monotonic_x =
831*593dc095SDavid du Colombier ((q->prev->pt.x <= s->p1.x && s->p1.x <= s->p2.x && s->p2.x <= s->pt.x) ||
832*593dc095SDavid du Colombier (q->prev->pt.x >= s->p1.x && s->p1.x >= s->p2.x && s->p2.x >= s->pt.x));
833*593dc095SDavid du Colombier } else
834*593dc095SDavid du Colombier q->monotonic_y = true;
835*593dc095SDavid du Colombier if (!q->monotonic_y) {
836*593dc095SDavid du Colombier curve_segment *s = (curve_segment *)q->pseg;
837*593dc095SDavid du Colombier int k = gx_curve_log2_samples(q->prev->pt.x, q->prev->pt.y, s, fo->fixed_flat);
838*593dc095SDavid du Colombier
839*593dc095SDavid du Colombier gx_flattened_iterator__init(q->fi, q->prev->pt.x, q->prev->pt.y, s, k);
840*593dc095SDavid du Colombier } else {
841*593dc095SDavid du Colombier q->dir = compute_dir(fo, q->prev->pt.y, q->pseg->pt.y);
842*593dc095SDavid du Colombier gx_flattened_iterator__init_line(q->fi,
843*593dc095SDavid du Colombier q->prev->pt.x, q->prev->pt.y, q->pseg->pt.x, q->pseg->pt.y); /* fake for curves. */
844*593dc095SDavid du Colombier vd_bar(q->prev->pt.x, q->prev->pt.y, q->pseg->pt.x, q->pseg->pt.y, 1, RGB(0, 0, 255));
845*593dc095SDavid du Colombier }
846*593dc095SDavid du Colombier q->first_flattened = true;
847*593dc095SDavid du Colombier }
848*593dc095SDavid du Colombier
849*593dc095SDavid du Colombier private int
scan_contour(line_list * ll,contour_cursor * q)850*593dc095SDavid du Colombier scan_contour(line_list *ll, contour_cursor *q)
851*593dc095SDavid du Colombier {
852*593dc095SDavid du Colombier contour_cursor p;
853*593dc095SDavid du Colombier gx_flattened_iterator fi, save_fi;
854*593dc095SDavid du Colombier segment *pseg;
855*593dc095SDavid du Colombier int code;
856*593dc095SDavid du Colombier bool only_horizontal = true, saved = false;
857*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
858*593dc095SDavid du Colombier contour_cursor save_q;
859*593dc095SDavid du Colombier
860*593dc095SDavid du Colombier p.fi = &fi;
861*593dc095SDavid du Colombier save_q.dir = 2;
862*593dc095SDavid du Colombier ll->main_dir = DIR_HORIZONTAL;
863*593dc095SDavid du Colombier for (; ; q->pseg = q->prev, q->prev = q->prev->prev) {
864*593dc095SDavid du Colombier init_contour_cursor(ll, q);
865*593dc095SDavid du Colombier while(gx_flattened_iterator__next(q->fi)) {
866*593dc095SDavid du Colombier q->first_flattened = false;
867*593dc095SDavid du Colombier q->dir = compute_dir(fo, q->fi->ly0, q->fi->ly1);
868*593dc095SDavid du Colombier ll->main_dir = (q->dir == DIR_DOWN ? DIR_DOWN :
869*593dc095SDavid du Colombier q->dir == DIR_UP ? DIR_UP : ll->main_dir);
870*593dc095SDavid du Colombier }
871*593dc095SDavid du Colombier q->dir = compute_dir(fo, q->fi->ly0, q->fi->ly1);
872*593dc095SDavid du Colombier q->more_flattened = false;
873*593dc095SDavid du Colombier ll->main_dir = (q->dir == DIR_DOWN ? DIR_DOWN :
874*593dc095SDavid du Colombier q->dir == DIR_UP ? DIR_UP : ll->main_dir);
875*593dc095SDavid du Colombier if (ll->main_dir != DIR_HORIZONTAL) {
876*593dc095SDavid du Colombier only_horizontal = false;
877*593dc095SDavid du Colombier break;
878*593dc095SDavid du Colombier }
879*593dc095SDavid du Colombier if (!saved && q->dir != 2) {
880*593dc095SDavid du Colombier save_q = *q;
881*593dc095SDavid du Colombier save_fi = *q->fi;
882*593dc095SDavid du Colombier saved = true;
883*593dc095SDavid du Colombier }
884*593dc095SDavid du Colombier if (q->prev == q->pfirst)
885*593dc095SDavid du Colombier break;
886*593dc095SDavid du Colombier }
887*593dc095SDavid du Colombier if (saved) {
888*593dc095SDavid du Colombier *q = save_q;
889*593dc095SDavid du Colombier *q->fi = save_fi;
890*593dc095SDavid du Colombier }
891*593dc095SDavid du Colombier for (pseg = q->pfirst; pseg != q->plast; pseg = pseg->next) {
892*593dc095SDavid du Colombier p.prev = pseg;
893*593dc095SDavid du Colombier p.pseg = pseg->next;
894*593dc095SDavid du Colombier if (!fo->pseudo_rasterization || only_horizontal
895*593dc095SDavid du Colombier || p.prev->pt.x != p.pseg->pt.x || p.prev->pt.y != p.pseg->pt.y
896*593dc095SDavid du Colombier || p.pseg->type == s_curve) {
897*593dc095SDavid du Colombier init_contour_cursor(ll, &p);
898*593dc095SDavid du Colombier p.more_flattened = gx_flattened_iterator__next(p.fi);
899*593dc095SDavid du Colombier p.dir = compute_dir(fo, p.fi->ly0, p.fi->ly1);
900*593dc095SDavid du Colombier if (p.fi->ly0 > fo->ymax && ll->y_break > p.fi->ly0)
901*593dc095SDavid du Colombier ll->y_break = p.fi->ly0;
902*593dc095SDavid du Colombier if (p.fi->ly1 > fo->ymax && ll->y_break > p.fi->ly1)
903*593dc095SDavid du Colombier ll->y_break = p.fi->ly1;
904*593dc095SDavid du Colombier if (p.monotonic_y && p.dir == DIR_HORIZONTAL &&
905*593dc095SDavid du Colombier !fo->pseudo_rasterization &&
906*593dc095SDavid du Colombier fixed2int_pixround(p.pseg->pt.y - fo->adjust_below) <
907*593dc095SDavid du Colombier fixed2int_pixround(p.pseg->pt.y + fo->adjust_above)) {
908*593dc095SDavid du Colombier /* Add it here to avoid double processing in process_h_segments. */
909*593dc095SDavid du Colombier code = add_y_line(p.prev, p.pseg, DIR_HORIZONTAL, ll);
910*593dc095SDavid du Colombier if (code < 0)
911*593dc095SDavid du Colombier return code;
912*593dc095SDavid du Colombier }
913*593dc095SDavid du Colombier if (p.monotonic_y && p.dir == DIR_HORIZONTAL &&
914*593dc095SDavid du Colombier fo->pseudo_rasterization && only_horizontal) {
915*593dc095SDavid du Colombier /* Add it here to avoid double processing in process_h_segments. */
916*593dc095SDavid du Colombier code = add_y_line(p.prev, p.pseg, DIR_HORIZONTAL, ll);
917*593dc095SDavid du Colombier if (code < 0)
918*593dc095SDavid du Colombier return code;
919*593dc095SDavid du Colombier }
920*593dc095SDavid du Colombier if (p.fi->ly0 >= fo->ymin && p.dir == DIR_UP && ll->main_dir == DIR_DOWN) {
921*593dc095SDavid du Colombier code = start_al_pair(ll, q, &p);
922*593dc095SDavid du Colombier if (code < 0)
923*593dc095SDavid du Colombier return code;
924*593dc095SDavid du Colombier }
925*593dc095SDavid du Colombier if (p.fi->ly0 < fo->ymin && p.fi->ly1 >= fo->ymin) {
926*593dc095SDavid du Colombier if (p.monotonic_y)
927*593dc095SDavid du Colombier code = add_y_line(p.prev, p.pseg, DIR_UP, ll);
928*593dc095SDavid du Colombier else
929*593dc095SDavid du Colombier code = add_y_curve_part(ll, p.prev, p.pseg, DIR_UP, p.fi,
930*593dc095SDavid du Colombier p.more_flattened, false, p.monotonic_x);
931*593dc095SDavid du Colombier if (code < 0)
932*593dc095SDavid du Colombier return code;
933*593dc095SDavid du Colombier }
934*593dc095SDavid du Colombier if (p.fi->ly0 >= fo->ymin && p.fi->ly1 < fo->ymin) {
935*593dc095SDavid du Colombier if (p.monotonic_y)
936*593dc095SDavid du Colombier code = add_y_line(p.prev, p.pseg, DIR_DOWN, ll);
937*593dc095SDavid du Colombier else
938*593dc095SDavid du Colombier code = add_y_curve_part(ll, p.prev, p.pseg, DIR_DOWN, p.fi,
939*593dc095SDavid du Colombier !p.first_flattened, false, p.monotonic_x);
940*593dc095SDavid du Colombier if (code < 0)
941*593dc095SDavid du Colombier return code;
942*593dc095SDavid du Colombier }
943*593dc095SDavid du Colombier ll->main_dir = (p.dir == DIR_DOWN ? DIR_DOWN :
944*593dc095SDavid du Colombier p.dir == DIR_UP ? DIR_UP : ll->main_dir);
945*593dc095SDavid du Colombier if (!p.monotonic_y && p.more_flattened) {
946*593dc095SDavid du Colombier code = start_al_pair_from_min(ll, &p);
947*593dc095SDavid du Colombier if (code < 0)
948*593dc095SDavid du Colombier return code;
949*593dc095SDavid du Colombier }
950*593dc095SDavid du Colombier if (p.dir == DIR_DOWN || p.dir == DIR_HORIZONTAL) {
951*593dc095SDavid du Colombier gx_flattened_iterator *fi1 = q->fi;
952*593dc095SDavid du Colombier q->prev = p.prev;
953*593dc095SDavid du Colombier q->pseg = p.pseg;
954*593dc095SDavid du Colombier q->monotonic_y = p.monotonic_y;
955*593dc095SDavid du Colombier q->more_flattened = p.more_flattened;
956*593dc095SDavid du Colombier q->first_flattened = p.first_flattened;
957*593dc095SDavid du Colombier q->fi = p.fi;
958*593dc095SDavid du Colombier q->dir = p.dir;
959*593dc095SDavid du Colombier p.fi = fi1;
960*593dc095SDavid du Colombier }
961*593dc095SDavid du Colombier }
962*593dc095SDavid du Colombier }
963*593dc095SDavid du Colombier q->fi = NULL; /* safety. */
964*593dc095SDavid du Colombier return 0;
965*593dc095SDavid du Colombier }
966*593dc095SDavid du Colombier
967*593dc095SDavid du Colombier /*
968*593dc095SDavid du Colombier * Construct a Y-sorted list of segments for rasterizing a path. We assume
969*593dc095SDavid du Colombier * the path is non-empty. Only include non-horizontal lines or (monotonic)
970*593dc095SDavid du Colombier * curve segments where one endpoint is locally Y-minimal, and horizontal
971*593dc095SDavid du Colombier * lines that might color some additional pixels.
972*593dc095SDavid du Colombier */
973*593dc095SDavid du Colombier private int
add_y_list(gx_path * ppath,line_list * ll)974*593dc095SDavid du Colombier add_y_list(gx_path * ppath, line_list *ll)
975*593dc095SDavid du Colombier {
976*593dc095SDavid du Colombier subpath *psub = ppath->first_subpath;
977*593dc095SDavid du Colombier int close_count = 0;
978*593dc095SDavid du Colombier int code;
979*593dc095SDavid du Colombier contour_cursor q;
980*593dc095SDavid du Colombier gx_flattened_iterator fi;
981*593dc095SDavid du Colombier
982*593dc095SDavid du Colombier ll->y_break = max_fixed;
983*593dc095SDavid du Colombier
984*593dc095SDavid du Colombier for (;psub; psub = (subpath *)psub->last->next) {
985*593dc095SDavid du Colombier /* We know that pseg points to a subpath head (s_start). */
986*593dc095SDavid du Colombier segment *pfirst = (segment *)psub;
987*593dc095SDavid du Colombier segment *plast = psub->last, *prev;
988*593dc095SDavid du Colombier
989*593dc095SDavid du Colombier q.fi = &fi;
990*593dc095SDavid du Colombier if (plast->type != s_line_close) {
991*593dc095SDavid du Colombier /* Create a fake s_line_close */
992*593dc095SDavid du Colombier line_close_segment *lp = &psub->closer;
993*593dc095SDavid du Colombier segment *next = plast->next;
994*593dc095SDavid du Colombier
995*593dc095SDavid du Colombier lp->next = next;
996*593dc095SDavid du Colombier lp->prev = plast;
997*593dc095SDavid du Colombier plast->next = (segment *) lp;
998*593dc095SDavid du Colombier if (next)
999*593dc095SDavid du Colombier next->prev = (segment *) lp;
1000*593dc095SDavid du Colombier lp->type = s_line_close;
1001*593dc095SDavid du Colombier lp->pt = psub->pt;
1002*593dc095SDavid du Colombier lp->sub = psub;
1003*593dc095SDavid du Colombier psub->last = plast = (segment *) lp;
1004*593dc095SDavid du Colombier ll->close_count++;
1005*593dc095SDavid du Colombier }
1006*593dc095SDavid du Colombier prev = plast->prev;
1007*593dc095SDavid du Colombier if (ll->fo->pseudo_rasterization && prev != pfirst &&
1008*593dc095SDavid du Colombier prev->pt.x == plast->pt.x && prev->pt.y == plast->pt.y) {
1009*593dc095SDavid du Colombier plast = prev;
1010*593dc095SDavid du Colombier prev = prev->prev;
1011*593dc095SDavid du Colombier }
1012*593dc095SDavid du Colombier q.prev = prev;
1013*593dc095SDavid du Colombier q.pseg = plast;
1014*593dc095SDavid du Colombier q.pfirst = pfirst;
1015*593dc095SDavid du Colombier q.plast = plast;
1016*593dc095SDavid du Colombier code = scan_contour(ll, &q);
1017*593dc095SDavid du Colombier if (code < 0)
1018*593dc095SDavid du Colombier return code;
1019*593dc095SDavid du Colombier }
1020*593dc095SDavid du Colombier return close_count;
1021*593dc095SDavid du Colombier }
1022*593dc095SDavid du Colombier
1023*593dc095SDavid du Colombier
1024*593dc095SDavid du Colombier private void
step_al(active_line * alp,bool move_iterator)1025*593dc095SDavid du Colombier step_al(active_line *alp, bool move_iterator)
1026*593dc095SDavid du Colombier {
1027*593dc095SDavid du Colombier bool forth = (alp->direction == DIR_UP || !alp->fi.curve);
1028*593dc095SDavid du Colombier
1029*593dc095SDavid du Colombier if (move_iterator) {
1030*593dc095SDavid du Colombier if (forth)
1031*593dc095SDavid du Colombier alp->more_flattened = gx_flattened_iterator__next(&alp->fi);
1032*593dc095SDavid du Colombier else
1033*593dc095SDavid du Colombier alp->more_flattened = gx_flattened_iterator__prev(&alp->fi);
1034*593dc095SDavid du Colombier } else
1035*593dc095SDavid du Colombier vd_bar(alp->fi.lx0, alp->fi.ly0, alp->fi.lx1, alp->fi.ly1, 1, RGB(0, 0, 255));
1036*593dc095SDavid du Colombier /* Note that we can get alp->fi.ly0 == alp->fi.ly1
1037*593dc095SDavid du Colombier if the curve tangent is horizontal. */
1038*593dc095SDavid du Colombier alp->start.x = (forth ? alp->fi.lx0 : alp->fi.lx1);
1039*593dc095SDavid du Colombier alp->start.y = (forth ? alp->fi.ly0 : alp->fi.ly1);
1040*593dc095SDavid du Colombier alp->end.x = (forth ? alp->fi.lx1 : alp->fi.lx0);
1041*593dc095SDavid du Colombier alp->end.y = (forth ? alp->fi.ly1 : alp->fi.ly0);
1042*593dc095SDavid du Colombier alp->diff.x = alp->end.x - alp->start.x;
1043*593dc095SDavid du Colombier alp->diff.y = alp->end.y - alp->start.y;
1044*593dc095SDavid du Colombier SET_NUM_ADJUST(alp);
1045*593dc095SDavid du Colombier (alp)->y_fast_max = MAX_MINUS_NUM_ADJUST(alp) /
1046*593dc095SDavid du Colombier ((alp->diff.x >= 0 ? alp->diff.x : -alp->diff.x) | 1) + alp->start.y;
1047*593dc095SDavid du Colombier }
1048*593dc095SDavid du Colombier
1049*593dc095SDavid du Colombier private void
init_al(active_line * alp,const segment * s0,const segment * s1,const line_list * ll)1050*593dc095SDavid du Colombier init_al(active_line *alp, const segment *s0, const segment *s1, const line_list *ll)
1051*593dc095SDavid du Colombier {
1052*593dc095SDavid du Colombier const segment *ss = (alp->direction == DIR_UP ? s1 : s0);
1053*593dc095SDavid du Colombier /* Warning : p0 may be equal to &alp->end. */
1054*593dc095SDavid du Colombier bool curve = (ss != NULL && ss->type == s_curve);
1055*593dc095SDavid du Colombier
1056*593dc095SDavid du Colombier if (curve) {
1057*593dc095SDavid du Colombier if (alp->direction == DIR_UP) {
1058*593dc095SDavid du Colombier const curve_segment *cs = (const curve_segment *)s1;
1059*593dc095SDavid du Colombier int k = gx_curve_log2_samples(s0->pt.x, s0->pt.y, cs, ll->fo->fixed_flat);
1060*593dc095SDavid du Colombier
1061*593dc095SDavid du Colombier gx_flattened_iterator__init(&alp->fi,
1062*593dc095SDavid du Colombier s0->pt.x, s0->pt.y, (const curve_segment *)s1, k);
1063*593dc095SDavid du Colombier step_al(alp, true);
1064*593dc095SDavid du Colombier if (!ll->fo->fill_by_trapezoids) {
1065*593dc095SDavid du Colombier alp->monotonic_y = (s0->pt.y <= cs->p1.y && cs->p1.y <= cs->p2.y && cs->p2.y <= cs->pt.y);
1066*593dc095SDavid du Colombier alp->monotonic_x = (s0->pt.x <= cs->p1.x && cs->p1.x <= cs->p2.x && cs->p2.x <= cs->pt.x) ||
1067*593dc095SDavid du Colombier (s0->pt.x >= cs->p1.x && cs->p1.x >= cs->p2.x && cs->p2.x >= cs->pt.x);
1068*593dc095SDavid du Colombier }
1069*593dc095SDavid du Colombier } else {
1070*593dc095SDavid du Colombier const curve_segment *cs = (const curve_segment *)s0;
1071*593dc095SDavid du Colombier int k = gx_curve_log2_samples(s1->pt.x, s1->pt.y, cs, ll->fo->fixed_flat);
1072*593dc095SDavid du Colombier bool more;
1073*593dc095SDavid du Colombier
1074*593dc095SDavid du Colombier gx_flattened_iterator__init(&alp->fi,
1075*593dc095SDavid du Colombier s1->pt.x, s1->pt.y, (const curve_segment *)s0, k);
1076*593dc095SDavid du Colombier alp->more_flattened = false;
1077*593dc095SDavid du Colombier do {
1078*593dc095SDavid du Colombier more = gx_flattened_iterator__next(&alp->fi);
1079*593dc095SDavid du Colombier alp->more_flattened |= more;
1080*593dc095SDavid du Colombier } while(more);
1081*593dc095SDavid du Colombier gx_flattened_iterator__switch_to_backscan(&alp->fi, alp->more_flattened);
1082*593dc095SDavid du Colombier step_al(alp, false);
1083*593dc095SDavid du Colombier if (!ll->fo->fill_by_trapezoids) {
1084*593dc095SDavid du Colombier alp->monotonic_y = (s0->pt.y >= cs->p1.y && cs->p1.y >= cs->p2.y && cs->p2.y >= cs->pt.y);
1085*593dc095SDavid du Colombier alp->monotonic_x = (s0->pt.x <= cs->p1.x && cs->p1.x <= cs->p2.x && cs->p2.x <= cs->pt.x) ||
1086*593dc095SDavid du Colombier (s0->pt.x >= cs->p1.x && cs->p1.x >= cs->p2.x && cs->p2.x >= cs->pt.x);
1087*593dc095SDavid du Colombier }
1088*593dc095SDavid du Colombier }
1089*593dc095SDavid du Colombier } else {
1090*593dc095SDavid du Colombier gx_flattened_iterator__init_line(&alp->fi,
1091*593dc095SDavid du Colombier s0->pt.x, s0->pt.y, s1->pt.x, s1->pt.y);
1092*593dc095SDavid du Colombier step_al(alp, true);
1093*593dc095SDavid du Colombier alp->monotonic_x = alp->monotonic_y = true;
1094*593dc095SDavid du Colombier }
1095*593dc095SDavid du Colombier alp->pseg = s1;
1096*593dc095SDavid du Colombier }
1097*593dc095SDavid du Colombier /*
1098*593dc095SDavid du Colombier * Internal routine to test a segment and add it to the pending list if
1099*593dc095SDavid du Colombier * appropriate.
1100*593dc095SDavid du Colombier */
1101*593dc095SDavid du Colombier private int
add_y_line_aux(const segment * prev_lp,const segment * lp,const gs_fixed_point * curr,const gs_fixed_point * prev,int dir,line_list * ll)1102*593dc095SDavid du Colombier add_y_line_aux(const segment * prev_lp, const segment * lp,
1103*593dc095SDavid du Colombier const gs_fixed_point *curr, const gs_fixed_point *prev, int dir, line_list *ll)
1104*593dc095SDavid du Colombier {
1105*593dc095SDavid du Colombier active_line *alp = make_al(ll);
1106*593dc095SDavid du Colombier if (alp == NULL)
1107*593dc095SDavid du Colombier return_error(gs_error_VMerror);
1108*593dc095SDavid du Colombier alp->more_flattened = false;
1109*593dc095SDavid du Colombier switch ((alp->direction = dir)) {
1110*593dc095SDavid du Colombier case DIR_UP:
1111*593dc095SDavid du Colombier init_al(alp, prev_lp, lp, ll);
1112*593dc095SDavid du Colombier break;
1113*593dc095SDavid du Colombier case DIR_DOWN:
1114*593dc095SDavid du Colombier init_al(alp, lp, prev_lp, ll);
1115*593dc095SDavid du Colombier break;
1116*593dc095SDavid du Colombier case DIR_HORIZONTAL:
1117*593dc095SDavid du Colombier alp->start = *prev;
1118*593dc095SDavid du Colombier alp->end = *curr;
1119*593dc095SDavid du Colombier /* Don't need to set dx or y_fast_max */
1120*593dc095SDavid du Colombier alp->pseg = prev_lp; /* may not need this either */
1121*593dc095SDavid du Colombier break;
1122*593dc095SDavid du Colombier default: /* can't happen */
1123*593dc095SDavid du Colombier return_error(gs_error_unregistered);
1124*593dc095SDavid du Colombier }
1125*593dc095SDavid du Colombier insert_y_line(ll, alp);
1126*593dc095SDavid du Colombier return 0;
1127*593dc095SDavid du Colombier }
1128*593dc095SDavid du Colombier
1129*593dc095SDavid du Colombier
11307dd7cddfSDavid du Colombier /* ---------------- Filling loop utilities ---------------- */
11317dd7cddfSDavid du Colombier
11327dd7cddfSDavid du Colombier /* Insert a newly active line in the X ordering. */
11337dd7cddfSDavid du Colombier private void
insert_x_new(active_line * alp,line_list * ll)1134*593dc095SDavid du Colombier insert_x_new(active_line * alp, line_list *ll)
11357dd7cddfSDavid du Colombier {
11363ff48bf5SDavid du Colombier active_line *next;
11373ff48bf5SDavid du Colombier active_line *prev = &ll->x_head;
11387dd7cddfSDavid du Colombier
1139*593dc095SDavid du Colombier vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 1, RGB(128, 128, 0));
11407dd7cddfSDavid du Colombier alp->x_current = alp->start.x;
1141*593dc095SDavid du Colombier alp->x_next = alp->start.x; /* If the spot starts with a horizontal segment,
1142*593dc095SDavid du Colombier we need resort_x_line to work properly
1143*593dc095SDavid du Colombier in the very beginning. */
11447dd7cddfSDavid du Colombier while (INCR_EXPR(x_step),
11457dd7cddfSDavid du Colombier (next = prev->next) != 0 && x_order(next, alp) < 0
11467dd7cddfSDavid du Colombier )
11477dd7cddfSDavid du Colombier prev = next;
11487dd7cddfSDavid du Colombier alp->next = next;
11497dd7cddfSDavid du Colombier alp->prev = prev;
11507dd7cddfSDavid du Colombier if (next != 0)
11517dd7cddfSDavid du Colombier next->prev = alp;
11527dd7cddfSDavid du Colombier prev->next = alp;
11537dd7cddfSDavid du Colombier }
11547dd7cddfSDavid du Colombier
1155*593dc095SDavid du Colombier /* Insert a newly active line in the h list. */
1156*593dc095SDavid du Colombier /* h list isn't ordered because x intervals may overlap. */
1157*593dc095SDavid du Colombier /* todo : optimize with maintaining ordered interval list;
1158*593dc095SDavid du Colombier Unite contacting inrevals, like we did in add_margin.
1159*593dc095SDavid du Colombier */
1160*593dc095SDavid du Colombier private inline void
insert_h_new(active_line * alp,line_list * ll)1161*593dc095SDavid du Colombier insert_h_new(active_line * alp, line_list *ll)
1162*593dc095SDavid du Colombier {
1163*593dc095SDavid du Colombier alp->next = ll->h_list0;
1164*593dc095SDavid du Colombier alp->prev = 0;
1165*593dc095SDavid du Colombier if (ll->h_list0 != 0)
1166*593dc095SDavid du Colombier ll->h_list0->prev = alp;
1167*593dc095SDavid du Colombier ll->h_list0 = alp;
1168*593dc095SDavid du Colombier /*
1169*593dc095SDavid du Colombier * h list keeps horizontal lines for a given y.
1170*593dc095SDavid du Colombier * There are 2 lists, corresponding to upper and lower ends
1171*593dc095SDavid du Colombier * of the Y-interval, which spot_into_trapezoids currently proceeds.
1172*593dc095SDavid du Colombier * Parts of horizontal lines, which do not contact a filled trapezoid,
1173*593dc095SDavid du Colombier * are parts of the spot bondairy. They to be marked in
1174*593dc095SDavid du Colombier * the 'sect' array. - see process_h_lists.
1175*593dc095SDavid du Colombier */
1176*593dc095SDavid du Colombier }
1177*593dc095SDavid du Colombier
1178*593dc095SDavid du Colombier private inline void
remove_al(const line_list * ll,active_line * alp)1179*593dc095SDavid du Colombier remove_al(const line_list *ll, active_line *alp)
1180*593dc095SDavid du Colombier {
1181*593dc095SDavid du Colombier active_line *nlp = alp->next;
1182*593dc095SDavid du Colombier
1183*593dc095SDavid du Colombier alp->prev->next = nlp;
1184*593dc095SDavid du Colombier if (nlp)
1185*593dc095SDavid du Colombier nlp->prev = alp->prev;
1186*593dc095SDavid du Colombier if_debug1('F', "[F]drop 0x%lx\n", (ulong) alp);
1187*593dc095SDavid du Colombier }
1188*593dc095SDavid du Colombier
11893ff48bf5SDavid du Colombier /*
11903ff48bf5SDavid du Colombier * Handle a line segment that just ended. Return true iff this was
11913ff48bf5SDavid du Colombier * the end of a line sequence.
11923ff48bf5SDavid du Colombier */
11937dd7cddfSDavid du Colombier private bool
end_x_line(active_line * alp,const line_list * ll,bool update)1194*593dc095SDavid du Colombier end_x_line(active_line *alp, const line_list *ll, bool update)
11957dd7cddfSDavid du Colombier {
11967dd7cddfSDavid du Colombier const segment *pseg = alp->pseg;
11977dd7cddfSDavid du Colombier /*
11987dd7cddfSDavid du Colombier * The computation of next relies on the fact that
11997dd7cddfSDavid du Colombier * all subpaths have been closed. When we cycle
12007dd7cddfSDavid du Colombier * around to the other end of a subpath, we must be
12017dd7cddfSDavid du Colombier * sure not to process the start/end point twice.
12027dd7cddfSDavid du Colombier */
12037dd7cddfSDavid du Colombier const segment *next =
12043ff48bf5SDavid du Colombier (alp->direction == DIR_UP ?
12057dd7cddfSDavid du Colombier ( /* Upward line, go forward along path. */
12067dd7cddfSDavid du Colombier pseg->type == s_line_close ? /* end of subpath */
12077dd7cddfSDavid du Colombier ((const line_close_segment *)pseg)->sub->next :
12087dd7cddfSDavid du Colombier pseg->next) :
12097dd7cddfSDavid du Colombier ( /* Downward line, go backward along path. */
12107dd7cddfSDavid du Colombier pseg->type == s_start ? /* start of subpath */
12117dd7cddfSDavid du Colombier ((const subpath *)pseg)->last->prev :
12127dd7cddfSDavid du Colombier pseg->prev)
12137dd7cddfSDavid du Colombier );
12147dd7cddfSDavid du Colombier
1215*593dc095SDavid du Colombier if (alp->end.y < alp->start.y) {
1216*593dc095SDavid du Colombier /* fixme: The condition above causes a horizontal
1217*593dc095SDavid du Colombier part of a curve near an Y maximum to process twice :
1218*593dc095SDavid du Colombier once scanning the left spot boundary and once scanning the right one.
1219*593dc095SDavid du Colombier In both cases it will go to the H list.
1220*593dc095SDavid du Colombier However the dropout prevention logic isn't
1221*593dc095SDavid du Colombier sensitive to that, and such segments does not affect
1222*593dc095SDavid du Colombier trapezoids. Thus the resulting raster doesn't depend on that.
1223*593dc095SDavid du Colombier However it would be nice to improve someday.
1224*593dc095SDavid du Colombier */
1225*593dc095SDavid du Colombier remove_al(ll, alp);
1226*593dc095SDavid du Colombier return true;
1227*593dc095SDavid du Colombier } else if (alp->more_flattened)
1228*593dc095SDavid du Colombier return false;
1229*593dc095SDavid du Colombier init_al(alp, pseg, next, ll);
1230*593dc095SDavid du Colombier if (alp->start.y > alp->end.y) {
1231*593dc095SDavid du Colombier /* See comment above. */
1232*593dc095SDavid du Colombier remove_al(ll, alp);
12337dd7cddfSDavid du Colombier return true;
12347dd7cddfSDavid du Colombier }
1235*593dc095SDavid du Colombier alp->x_current = alp->x_next = alp->start.x;
1236*593dc095SDavid du Colombier vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 1, RGB(128, 0, 128));
12377dd7cddfSDavid du Colombier print_al("repl", alp);
12387dd7cddfSDavid du Colombier return false;
12397dd7cddfSDavid du Colombier }
12407dd7cddfSDavid du Colombier
1241*593dc095SDavid du Colombier private inline int
add_margin(line_list * ll,active_line * flp,active_line * alp,fixed y0,fixed y1)1242*593dc095SDavid du Colombier add_margin(line_list * ll, active_line * flp, active_line * alp, fixed y0, fixed y1)
1243*593dc095SDavid du Colombier { vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 1, RGB(255, 255, 255));
1244*593dc095SDavid du Colombier vd_bar(flp->start.x, flp->start.y, flp->end.x, flp->end.y, 1, RGB(255, 255, 255));
1245*593dc095SDavid du Colombier return continue_margin_common(ll, &ll->margin_set0, flp, alp, y0, y1);
1246*593dc095SDavid du Colombier }
12477dd7cddfSDavid du Colombier
1248*593dc095SDavid du Colombier private inline int
continue_margin(line_list * ll,active_line * flp,active_line * alp,fixed y0,fixed y1)1249*593dc095SDavid du Colombier continue_margin(line_list * ll, active_line * flp, active_line * alp, fixed y0, fixed y1)
12507dd7cddfSDavid du Colombier {
1251*593dc095SDavid du Colombier return continue_margin_common(ll, &ll->margin_set0, flp, alp, y0, y1);
1252*593dc095SDavid du Colombier }
1253*593dc095SDavid du Colombier
1254*593dc095SDavid du Colombier private inline int
complete_margin(line_list * ll,active_line * flp,active_line * alp,fixed y0,fixed y1)1255*593dc095SDavid du Colombier complete_margin(line_list * ll, active_line * flp, active_line * alp, fixed y0, fixed y1)
1256*593dc095SDavid du Colombier {
1257*593dc095SDavid du Colombier return continue_margin_common(ll, &ll->margin_set1, flp, alp, y0, y1);
1258*593dc095SDavid du Colombier }
1259*593dc095SDavid du Colombier
1260*593dc095SDavid du Colombier /*
1261*593dc095SDavid du Colombier * Handle the case of a slanted trapezoid with adjustment.
1262*593dc095SDavid du Colombier * To do this exactly right requires filling a central trapezoid
1263*593dc095SDavid du Colombier * (or rectangle) plus two horizontal almost-rectangles.
1264*593dc095SDavid du Colombier */
1265*593dc095SDavid du Colombier private int
fill_slant_adjust(const line_list * ll,const active_line * flp,const active_line * alp,fixed y,fixed y1)1266*593dc095SDavid du Colombier fill_slant_adjust(const line_list *ll,
1267*593dc095SDavid du Colombier const active_line *flp, const active_line *alp, fixed y, fixed y1)
1268*593dc095SDavid du Colombier {
1269*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
1270*593dc095SDavid du Colombier const fixed Yb = y - fo->adjust_below;
1271*593dc095SDavid du Colombier const fixed Ya = y + fo->adjust_above;
1272*593dc095SDavid du Colombier const fixed Y1b = y1 - fo->adjust_below;
1273*593dc095SDavid du Colombier const fixed Y1a = y1 + fo->adjust_above;
1274*593dc095SDavid du Colombier const gs_fixed_edge *plbot, *prbot, *pltop, *prtop;
1275*593dc095SDavid du Colombier gs_fixed_edge vert_left, slant_left, vert_right, slant_right;
1276*593dc095SDavid du Colombier int code;
1277*593dc095SDavid du Colombier
1278*593dc095SDavid du Colombier INCR(slant);
1279*593dc095SDavid du Colombier vd_quad(flp->x_current, y, alp->x_current, y,
1280*593dc095SDavid du Colombier alp->x_next, y1, flp->x_next, y1, 1, VD_TRAP_COLOR); /* fixme: Wrong X. */
1281*593dc095SDavid du Colombier
1282*593dc095SDavid du Colombier /* Set up all the edges, even though we may not need them all. */
1283*593dc095SDavid du Colombier
1284*593dc095SDavid du Colombier if (flp->start.x < flp->end.x) {
1285*593dc095SDavid du Colombier vert_left.start.x = vert_left.end.x = flp->x_current - fo->adjust_left;
1286*593dc095SDavid du Colombier vert_left.start.y = Yb, vert_left.end.y = Ya;
1287*593dc095SDavid du Colombier vert_right.start.x = vert_right.end.x = alp->x_next + fo->adjust_right;
1288*593dc095SDavid du Colombier vert_right.start.y = Y1b, vert_right.end.y = Y1a;
1289*593dc095SDavid du Colombier slant_left.start.y = flp->start.y + fo->adjust_above;
1290*593dc095SDavid du Colombier slant_left.end.y = flp->end.y + fo->adjust_above;
1291*593dc095SDavid du Colombier slant_right.start.y = alp->start.y - fo->adjust_below;
1292*593dc095SDavid du Colombier slant_right.end.y = alp->end.y - fo->adjust_below;
1293*593dc095SDavid du Colombier plbot = &vert_left, prbot = &slant_right;
1294*593dc095SDavid du Colombier pltop = &slant_left, prtop = &vert_right;
1295*593dc095SDavid du Colombier } else {
1296*593dc095SDavid du Colombier vert_left.start.x = vert_left.end.x = flp->x_next - fo->adjust_left;
1297*593dc095SDavid du Colombier vert_left.start.y = Y1b, vert_left.end.y = Y1a;
1298*593dc095SDavid du Colombier vert_right.start.x = vert_right.end.x = alp->x_current + fo->adjust_right;
1299*593dc095SDavid du Colombier vert_right.start.y = Yb, vert_right.end.y = Ya;
1300*593dc095SDavid du Colombier slant_left.start.y = flp->start.y - fo->adjust_below;
1301*593dc095SDavid du Colombier slant_left.end.y = flp->end.y - fo->adjust_below;
1302*593dc095SDavid du Colombier slant_right.start.y = alp->start.y + fo->adjust_above;
1303*593dc095SDavid du Colombier slant_right.end.y = alp->end.y + fo->adjust_above;
1304*593dc095SDavid du Colombier plbot = &slant_left, prbot = &vert_right;
1305*593dc095SDavid du Colombier pltop = &vert_left, prtop = &slant_right;
1306*593dc095SDavid du Colombier }
1307*593dc095SDavid du Colombier slant_left.start.x = flp->start.x - fo->adjust_left;
1308*593dc095SDavid du Colombier slant_left.end.x = flp->end.x - fo->adjust_left;
1309*593dc095SDavid du Colombier slant_right.start.x = alp->start.x + fo->adjust_right;
1310*593dc095SDavid du Colombier slant_right.end.x = alp->end.x + fo->adjust_right;
1311*593dc095SDavid du Colombier
1312*593dc095SDavid du Colombier if (Ya >= Y1b) {
1313*593dc095SDavid du Colombier /*
1314*593dc095SDavid du Colombier * The upper and lower adjustment bands overlap.
1315*593dc095SDavid du Colombier * Since the entire entity is less than 2 pixels high
1316*593dc095SDavid du Colombier * in this case, we could handle it very efficiently
1317*593dc095SDavid du Colombier * with no more than 2 rectangle fills, but for right now
1318*593dc095SDavid du Colombier * we don't attempt to do this.
1319*593dc095SDavid du Colombier */
1320*593dc095SDavid du Colombier int iYb = fixed2int_var_pixround(Yb);
1321*593dc095SDavid du Colombier int iYa = fixed2int_var_pixround(Ya);
1322*593dc095SDavid du Colombier int iY1b = fixed2int_var_pixround(Y1b);
1323*593dc095SDavid du Colombier int iY1a = fixed2int_var_pixround(Y1a);
1324*593dc095SDavid du Colombier
1325*593dc095SDavid du Colombier INCR(slant_shallow);
1326*593dc095SDavid du Colombier if (iY1b > iYb) {
1327*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, plbot, prbot,
1328*593dc095SDavid du Colombier Yb, Y1b, false, fo->pdevc, fo->lop);
1329*593dc095SDavid du Colombier if (code < 0)
1330*593dc095SDavid du Colombier return code;
1331*593dc095SDavid du Colombier }
1332*593dc095SDavid du Colombier if (iYa > iY1b) {
1333*593dc095SDavid du Colombier int ix = fixed2int_var_pixround(vert_left.start.x);
1334*593dc095SDavid du Colombier int iw = fixed2int_var_pixround(vert_right.start.x) - ix;
1335*593dc095SDavid du Colombier
1336*593dc095SDavid du Colombier code = gx_fill_rectangle_device_rop(ix, iY1b, iw, iYa - iY1b,
1337*593dc095SDavid du Colombier fo->pdevc, fo->dev, fo->lop);
1338*593dc095SDavid du Colombier if (code < 0)
1339*593dc095SDavid du Colombier return code;
1340*593dc095SDavid du Colombier }
1341*593dc095SDavid du Colombier if (iY1a > iYa)
1342*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, pltop, prtop,
1343*593dc095SDavid du Colombier Ya, Y1a, false, fo->pdevc, fo->lop);
1344*593dc095SDavid du Colombier else
1345*593dc095SDavid du Colombier code = 0;
1346*593dc095SDavid du Colombier } else {
1347*593dc095SDavid du Colombier /*
1348*593dc095SDavid du Colombier * Clip the trapezoid if possible. This can save a lot
1349*593dc095SDavid du Colombier * of work when filling paths that cross band boundaries.
1350*593dc095SDavid du Colombier */
1351*593dc095SDavid du Colombier fixed Yac;
1352*593dc095SDavid du Colombier
1353*593dc095SDavid du Colombier if (fo->pbox->p.y < Ya) {
1354*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, plbot, prbot,
1355*593dc095SDavid du Colombier Yb, Ya, false, fo->pdevc, fo->lop);
1356*593dc095SDavid du Colombier if (code < 0)
1357*593dc095SDavid du Colombier return code;
1358*593dc095SDavid du Colombier Yac = Ya;
1359*593dc095SDavid du Colombier } else
1360*593dc095SDavid du Colombier Yac = fo->pbox->p.y;
1361*593dc095SDavid du Colombier if (fo->pbox->q.y > Y1b) {
1362*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, &slant_left, &slant_right,
1363*593dc095SDavid du Colombier Yac, Y1b, false, fo->pdevc, fo->lop);
1364*593dc095SDavid du Colombier if (code < 0)
1365*593dc095SDavid du Colombier return code;
1366*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, pltop, prtop,
1367*593dc095SDavid du Colombier Y1b, Y1a, false, fo->pdevc, fo->lop);
1368*593dc095SDavid du Colombier } else
1369*593dc095SDavid du Colombier code = fo->fill_trap(fo->dev, &slant_left, &slant_right,
1370*593dc095SDavid du Colombier Yac, fo->pbox->q.y, false, fo->pdevc, fo->lop);
1371*593dc095SDavid du Colombier }
1372*593dc095SDavid du Colombier return code;
1373*593dc095SDavid du Colombier }
1374*593dc095SDavid du Colombier
1375*593dc095SDavid du Colombier /* Re-sort the x list by moving alp backward to its proper spot. */
1376*593dc095SDavid du Colombier private inline void
resort_x_line(active_line * alp)1377*593dc095SDavid du Colombier resort_x_line(active_line * alp)
1378*593dc095SDavid du Colombier {
1379*593dc095SDavid du Colombier active_line *prev = alp->prev;
1380*593dc095SDavid du Colombier active_line *next = alp->next;
1381*593dc095SDavid du Colombier
1382*593dc095SDavid du Colombier prev->next = next;
1383*593dc095SDavid du Colombier if (next)
1384*593dc095SDavid du Colombier next->prev = prev;
1385*593dc095SDavid du Colombier while (x_order(prev, alp) > 0) {
1386*593dc095SDavid du Colombier if_debug2('F', "[F]swap 0x%lx,0x%lx\n",
1387*593dc095SDavid du Colombier (ulong) alp, (ulong) prev);
1388*593dc095SDavid du Colombier next = prev, prev = prev->prev;
1389*593dc095SDavid du Colombier }
1390*593dc095SDavid du Colombier alp->next = next;
1391*593dc095SDavid du Colombier alp->prev = prev;
1392*593dc095SDavid du Colombier /* next might be null, if alp was in the correct spot already. */
1393*593dc095SDavid du Colombier if (next)
1394*593dc095SDavid du Colombier next->prev = alp;
1395*593dc095SDavid du Colombier prev->next = alp;
1396*593dc095SDavid du Colombier }
1397*593dc095SDavid du Colombier
1398*593dc095SDavid du Colombier /* Move active lines by Y. */
1399*593dc095SDavid du Colombier private inline void
move_al_by_y(line_list * ll,fixed y1)1400*593dc095SDavid du Colombier move_al_by_y(line_list *ll, fixed y1)
1401*593dc095SDavid du Colombier {
1402*593dc095SDavid du Colombier fixed x;
1403*593dc095SDavid du Colombier active_line *alp, *nlp;
1404*593dc095SDavid du Colombier
1405*593dc095SDavid du Colombier for (x = min_fixed, alp = ll->x_list; alp != 0; alp = nlp) {
1406*593dc095SDavid du Colombier bool notend = false;
1407*593dc095SDavid du Colombier alp->x_current = alp->x_next;
1408*593dc095SDavid du Colombier
1409*593dc095SDavid du Colombier nlp = alp->next;
1410*593dc095SDavid du Colombier if (alp->end.y == y1 && alp->more_flattened) {
1411*593dc095SDavid du Colombier step_al(alp, true);
1412*593dc095SDavid du Colombier alp->x_current = alp->x_next = alp->start.x;
1413*593dc095SDavid du Colombier notend = (alp->end.y >= alp->start.y);
1414*593dc095SDavid du Colombier }
1415*593dc095SDavid du Colombier if (alp->end.y > y1 || notend || !end_x_line(alp, ll, true)) {
1416*593dc095SDavid du Colombier if (alp->x_next <= x)
1417*593dc095SDavid du Colombier resort_x_line(alp);
1418*593dc095SDavid du Colombier else
1419*593dc095SDavid du Colombier x = alp->x_next;
1420*593dc095SDavid du Colombier }
1421*593dc095SDavid du Colombier }
1422*593dc095SDavid du Colombier if (ll->x_list != 0 && ll->fo->pseudo_rasterization) {
1423*593dc095SDavid du Colombier /* Ensure that contacting vertical stems are properly ordered.
1424*593dc095SDavid du Colombier We don't want to unite contacting stems into
1425*593dc095SDavid du Colombier a single margin, because it can cause a dropout :
1426*593dc095SDavid du Colombier narrow stems are widened against a dropout, but
1427*593dc095SDavid du Colombier an united wide one may be left unwidened.
1428*593dc095SDavid du Colombier */
1429*593dc095SDavid du Colombier for (alp = ll->x_list; alp->next != 0; ) {
1430*593dc095SDavid du Colombier if (alp->start.x == alp->end.x &&
1431*593dc095SDavid du Colombier alp->start.x == alp->next->start.x &&
1432*593dc095SDavid du Colombier alp->next->start.x == alp->next->end.x &&
1433*593dc095SDavid du Colombier alp->direction > alp->next->direction) {
1434*593dc095SDavid du Colombier /* Exchange. */
1435*593dc095SDavid du Colombier active_line *prev = alp->prev;
1436*593dc095SDavid du Colombier active_line *next = alp->next;
1437*593dc095SDavid du Colombier active_line *next2 = next->next;
1438*593dc095SDavid du Colombier if (prev)
1439*593dc095SDavid du Colombier prev->next = next;
1440*593dc095SDavid du Colombier else
1441*593dc095SDavid du Colombier ll->x_list = next;
1442*593dc095SDavid du Colombier next->prev = prev;
1443*593dc095SDavid du Colombier alp->prev = next;
1444*593dc095SDavid du Colombier alp->next = next2;
1445*593dc095SDavid du Colombier next->next = alp;
1446*593dc095SDavid du Colombier if (next2)
1447*593dc095SDavid du Colombier next2->prev = alp;
1448*593dc095SDavid du Colombier } else
1449*593dc095SDavid du Colombier alp = alp->next;
1450*593dc095SDavid du Colombier }
1451*593dc095SDavid du Colombier }
1452*593dc095SDavid du Colombier }
1453*593dc095SDavid du Colombier
1454*593dc095SDavid du Colombier /* Process horizontal segment of curves. */
1455*593dc095SDavid du Colombier private inline int
process_h_segments(line_list * ll,fixed y)1456*593dc095SDavid du Colombier process_h_segments(line_list *ll, fixed y)
1457*593dc095SDavid du Colombier {
1458*593dc095SDavid du Colombier active_line *alp, *nlp;
1459*593dc095SDavid du Colombier int code, inserted = 0;
1460*593dc095SDavid du Colombier
1461*593dc095SDavid du Colombier for (alp = ll->x_list; alp != 0; alp = nlp) {
1462*593dc095SDavid du Colombier nlp = alp->next;
1463*593dc095SDavid du Colombier if (alp->start.y == y && alp->end.y == y) {
1464*593dc095SDavid du Colombier if (ll->fo->pseudo_rasterization) {
1465*593dc095SDavid du Colombier code = add_y_line_aux(NULL, NULL, &alp->start, &alp->end, DIR_HORIZONTAL, ll);
1466*593dc095SDavid du Colombier if (code < 0)
1467*593dc095SDavid du Colombier return code;
1468*593dc095SDavid du Colombier }
1469*593dc095SDavid du Colombier inserted = 1;
1470*593dc095SDavid du Colombier }
1471*593dc095SDavid du Colombier }
1472*593dc095SDavid du Colombier return inserted;
1473*593dc095SDavid du Colombier /* After this should call move_al_by_y and step to the next band. */
1474*593dc095SDavid du Colombier }
1475*593dc095SDavid du Colombier
1476*593dc095SDavid du Colombier private inline int
loop_fill_trap_np(const line_list * ll,const gs_fixed_edge * le,const gs_fixed_edge * re,fixed y,fixed y1)1477*593dc095SDavid du Colombier loop_fill_trap_np(const line_list *ll, const gs_fixed_edge *le, const gs_fixed_edge *re, fixed y, fixed y1)
1478*593dc095SDavid du Colombier {
1479*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
1480*593dc095SDavid du Colombier fixed ybot = max(y, fo->pbox->p.y);
1481*593dc095SDavid du Colombier fixed ytop = min(y1, fo->pbox->q.y);
14827dd7cddfSDavid du Colombier
14837dd7cddfSDavid du Colombier if (ybot >= ytop)
14847dd7cddfSDavid du Colombier return 0;
1485*593dc095SDavid du Colombier vd_quad(le->start.x, ybot, re->start.x, ybot, re->end.x, ytop, le->end.x, ytop, 1, VD_TRAP_COLOR);
1486*593dc095SDavid du Colombier return (*fo->fill_trap)
1487*593dc095SDavid du Colombier (fo->dev, le, re, ybot, ytop, false, fo->pdevc, fo->lop);
14887dd7cddfSDavid du Colombier }
14897dd7cddfSDavid du Colombier
1490*593dc095SDavid du Colombier /* Define variants of the algorithm for filling a slanted trapezoid. */
1491*593dc095SDavid du Colombier #define TEMPLATE_slant_into_trapezoids slant_into_trapezoids__fd
1492*593dc095SDavid du Colombier #define FILL_DIRECT 1
1493*593dc095SDavid du Colombier #include "gxfillts.h"
1494*593dc095SDavid du Colombier #undef TEMPLATE_slant_into_trapezoids
1495*593dc095SDavid du Colombier #undef FILL_DIRECT
14967dd7cddfSDavid du Colombier
1497*593dc095SDavid du Colombier #define TEMPLATE_slant_into_trapezoids slant_into_trapezoids__nd
1498*593dc095SDavid du Colombier #define FILL_DIRECT 0
1499*593dc095SDavid du Colombier #include "gxfillts.h"
1500*593dc095SDavid du Colombier #undef TEMPLATE_slant_into_trapezoids
1501*593dc095SDavid du Colombier #undef FILL_DIRECT
15027dd7cddfSDavid du Colombier
15037dd7cddfSDavid du Colombier
1504*593dc095SDavid du Colombier #define COVERING_PIXEL_CENTERS(y, y1, adjust_below, adjust_above)\
15057dd7cddfSDavid du Colombier (fixed_pixround(y - adjust_below) < fixed_pixround(y1 + adjust_above))
15067dd7cddfSDavid du Colombier
1507*593dc095SDavid du Colombier /* Find an intersection within y, y1. */
1508*593dc095SDavid du Colombier /* lp->x_current, lp->x_next must be set for y, y1. */
1509*593dc095SDavid du Colombier private bool
intersect(active_line * endp,active_line * alp,fixed y,fixed y1,fixed * p_y_new)1510*593dc095SDavid du Colombier intersect(active_line *endp, active_line *alp, fixed y, fixed y1, fixed *p_y_new)
1511*593dc095SDavid du Colombier {
1512*593dc095SDavid du Colombier fixed y_new, dy;
1513*593dc095SDavid du Colombier fixed dx_old = alp->x_current - endp->x_current;
1514*593dc095SDavid du Colombier fixed dx_den = dx_old + endp->x_next - alp->x_next;
15157dd7cddfSDavid du Colombier
1516*593dc095SDavid du Colombier if (dx_den <= dx_old)
1517*593dc095SDavid du Colombier return false; /* Intersection isn't possible. */
1518*593dc095SDavid du Colombier dy = y1 - y;
15197dd7cddfSDavid du Colombier if_debug3('F', "[F]cross: dy=%g, dx_old=%g, dx_new=%g\n",
15207dd7cddfSDavid du Colombier fixed2float(dy), fixed2float(dx_old),
15217dd7cddfSDavid du Colombier fixed2float(dx_den - dx_old));
15227dd7cddfSDavid du Colombier /* Do the computation in single precision */
15237dd7cddfSDavid du Colombier /* if the values are small enough. */
15247dd7cddfSDavid du Colombier y_new =
15257dd7cddfSDavid du Colombier ((dy | dx_old) < 1L << (size_of(fixed) * 4 - 1) ?
15267dd7cddfSDavid du Colombier dy * dx_old / dx_den :
15277dd7cddfSDavid du Colombier (INCR_EXPR(mq_cross), fixed_mult_quo(dy, dx_old, dx_den)))
15287dd7cddfSDavid du Colombier + y;
15297dd7cddfSDavid du Colombier /* The crossing value doesn't have to be */
15307dd7cddfSDavid du Colombier /* very accurate, but it does have to be */
15317dd7cddfSDavid du Colombier /* greater than y and less than y1. */
15327dd7cddfSDavid du Colombier if_debug3('F', "[F]cross y=%g, y_new=%g, y1=%g\n",
15337dd7cddfSDavid du Colombier fixed2float(y), fixed2float(y_new),
15347dd7cddfSDavid du Colombier fixed2float(y1));
15357dd7cddfSDavid du Colombier if (y_new <= y) {
15367dd7cddfSDavid du Colombier /*
15377dd7cddfSDavid du Colombier * This isn't possible. Recompute the intersection
15387dd7cddfSDavid du Colombier * accurately.
15397dd7cddfSDavid du Colombier */
15407dd7cddfSDavid du Colombier fixed ys, xs0, xs1, ye, xe0, xe1, dy, dx0, dx1;
15417dd7cddfSDavid du Colombier
15427dd7cddfSDavid du Colombier INCR(cross_slow);
15437dd7cddfSDavid du Colombier if (endp->start.y < alp->start.y)
15447dd7cddfSDavid du Colombier ys = alp->start.y,
15453ff48bf5SDavid du Colombier xs0 = AL_X_AT_Y(endp, ys), xs1 = alp->start.x;
15467dd7cddfSDavid du Colombier else
15477dd7cddfSDavid du Colombier ys = endp->start.y,
15483ff48bf5SDavid du Colombier xs0 = endp->start.x, xs1 = AL_X_AT_Y(alp, ys);
15497dd7cddfSDavid du Colombier if (endp->end.y > alp->end.y)
15507dd7cddfSDavid du Colombier ye = alp->end.y,
15513ff48bf5SDavid du Colombier xe0 = AL_X_AT_Y(endp, ye), xe1 = alp->end.x;
15527dd7cddfSDavid du Colombier else
15537dd7cddfSDavid du Colombier ye = endp->end.y,
15543ff48bf5SDavid du Colombier xe0 = endp->end.x, xe1 = AL_X_AT_Y(alp, ye);
15557dd7cddfSDavid du Colombier dy = ye - ys;
15567dd7cddfSDavid du Colombier dx0 = xe0 - xs0;
15577dd7cddfSDavid du Colombier dx1 = xe1 - xs1;
15587dd7cddfSDavid du Colombier /* We need xs0 + cross * dx0 == xs1 + cross * dx1. */
15597dd7cddfSDavid du Colombier if (dx0 == dx1) {
15607dd7cddfSDavid du Colombier /* The two lines are coincident. Do nothing. */
15617dd7cddfSDavid du Colombier y_new = y1;
15627dd7cddfSDavid du Colombier } else {
15637dd7cddfSDavid du Colombier double cross = (double)(xs0 - xs1) / (dx1 - dx0);
15647dd7cddfSDavid du Colombier
15657dd7cddfSDavid du Colombier y_new = (fixed)(ys + cross * dy);
15667dd7cddfSDavid du Colombier if (y_new <= y) {
15677dd7cddfSDavid du Colombier /*
15687dd7cddfSDavid du Colombier * This can only happen through some kind of
15697dd7cddfSDavid du Colombier * numeric disaster, but we have to check.
15707dd7cddfSDavid du Colombier */
15717dd7cddfSDavid du Colombier INCR(cross_low);
15727dd7cddfSDavid du Colombier y_new = y + fixed_epsilon;
15737dd7cddfSDavid du Colombier }
15747dd7cddfSDavid du Colombier }
15757dd7cddfSDavid du Colombier }
1576*593dc095SDavid du Colombier *p_y_new = y_new;
1577*593dc095SDavid du Colombier return true;
1578*593dc095SDavid du Colombier }
1579*593dc095SDavid du Colombier
1580*593dc095SDavid du Colombier private inline void
set_x_next(active_line * endp,active_line * alp,fixed x)1581*593dc095SDavid du Colombier set_x_next(active_line *endp, active_line *alp, fixed x)
1582*593dc095SDavid du Colombier {
1583*593dc095SDavid du Colombier while(endp != alp) {
1584*593dc095SDavid du Colombier endp->x_next = x;
1585*593dc095SDavid du Colombier endp = endp->next;
1586*593dc095SDavid du Colombier }
1587*593dc095SDavid du Colombier }
1588*593dc095SDavid du Colombier
1589*593dc095SDavid du Colombier private inline int
coord_weight(const active_line * alp)1590*593dc095SDavid du Colombier coord_weight(const active_line *alp)
1591*593dc095SDavid du Colombier {
1592*593dc095SDavid du Colombier return 1 + min(any_abs((int)((int64_t)alp->diff.y * 8 / alp->diff.x)), 256);
1593*593dc095SDavid du Colombier }
1594*593dc095SDavid du Colombier
1595*593dc095SDavid du Colombier
1596*593dc095SDavid du Colombier /* Find intersections of active lines within the band.
1597*593dc095SDavid du Colombier Intersect and reorder them, and correct the bund top. */
1598*593dc095SDavid du Colombier private void
intersect_al(line_list * ll,fixed y,fixed * y_top,int draw,bool all_bands)1599*593dc095SDavid du Colombier intersect_al(line_list *ll, fixed y, fixed *y_top, int draw, bool all_bands)
1600*593dc095SDavid du Colombier {
1601*593dc095SDavid du Colombier fixed x = min_fixed, y1 = *y_top;
1602*593dc095SDavid du Colombier active_line *alp, *stopx = NULL;
1603*593dc095SDavid du Colombier active_line *endp = NULL;
1604*593dc095SDavid du Colombier
1605*593dc095SDavid du Colombier /* don't bother if no pixels with no pseudo_rasterization */
1606*593dc095SDavid du Colombier if (y == y1) {
1607*593dc095SDavid du Colombier /* Rather the intersection algorithm can handle this case with
1608*593dc095SDavid du Colombier retrieving x_next equal to x_current,
1609*593dc095SDavid du Colombier we bypass it for safety reason.
1610*593dc095SDavid du Colombier */
1611*593dc095SDavid du Colombier } else if (ll->fo->pseudo_rasterization || draw >= 0 || all_bands) {
1612*593dc095SDavid du Colombier /*
1613*593dc095SDavid du Colombier * Loop invariants:
1614*593dc095SDavid du Colombier * alp = endp->next;
1615*593dc095SDavid du Colombier * for all lines lp from stopx up to alp,
1616*593dc095SDavid du Colombier * lp->x_next = AL_X_AT_Y(lp, y1).
1617*593dc095SDavid du Colombier */
1618*593dc095SDavid du Colombier for (alp = stopx = ll->x_list;
1619*593dc095SDavid du Colombier INCR_EXPR(find_y), alp != 0;
1620*593dc095SDavid du Colombier endp = alp, alp = alp->next
1621*593dc095SDavid du Colombier ) {
1622*593dc095SDavid du Colombier fixed nx = AL_X_AT_Y(alp, y1);
1623*593dc095SDavid du Colombier fixed y_new;
1624*593dc095SDavid du Colombier
1625*593dc095SDavid du Colombier alp->x_next = nx;
1626*593dc095SDavid du Colombier /* Check for intersecting lines. */
1627*593dc095SDavid du Colombier if (nx >= x)
1628*593dc095SDavid du Colombier x = nx;
1629*593dc095SDavid du Colombier else if (alp->x_current >= endp->x_current &&
1630*593dc095SDavid du Colombier intersect(endp, alp, y, y1, &y_new)) {
1631*593dc095SDavid du Colombier if (y_new <= y1) {
1632*593dc095SDavid du Colombier stopx = endp;
16337dd7cddfSDavid du Colombier y1 = y_new;
1634*593dc095SDavid du Colombier if (endp->diff.x == 0)
1635*593dc095SDavid du Colombier nx = endp->start.x;
1636*593dc095SDavid du Colombier else if (alp->diff.x == 0)
1637*593dc095SDavid du Colombier nx = alp->start.x;
1638*593dc095SDavid du Colombier else {
1639*593dc095SDavid du Colombier fixed nx0 = AL_X_AT_Y(endp, y1);
1640*593dc095SDavid du Colombier fixed nx1 = AL_X_AT_Y(alp, y1);
1641*593dc095SDavid du Colombier if (nx0 != nx1) {
1642*593dc095SDavid du Colombier /* Different results due to arithmetic errors.
1643*593dc095SDavid du Colombier Choose an imtermediate point.
1644*593dc095SDavid du Colombier We don't like to use floating numbners here,
1645*593dc095SDavid du Colombier but the code with int64_t isn't much better. */
1646*593dc095SDavid du Colombier int64_t w0 = coord_weight(endp);
1647*593dc095SDavid du Colombier int64_t w1 = coord_weight(alp);
1648*593dc095SDavid du Colombier
1649*593dc095SDavid du Colombier nx = (fixed)((w0 * nx0 + w1 * nx1) / (w0 + w1));
1650*593dc095SDavid du Colombier } else
1651*593dc095SDavid du Colombier nx = nx0;
1652*593dc095SDavid du Colombier }
1653*593dc095SDavid du Colombier endp->x_next = alp->x_next = nx; /* Ensure same X. */
16547dd7cddfSDavid du Colombier draw = 0;
1655*593dc095SDavid du Colombier /* Can't guarantee same x for triple intersections here.
1656*593dc095SDavid du Colombier Will take care below */
16577dd7cddfSDavid du Colombier }
16587dd7cddfSDavid du Colombier if (nx > x)
16597dd7cddfSDavid du Colombier x = nx;
16607dd7cddfSDavid du Colombier }
16617dd7cddfSDavid du Colombier }
16627dd7cddfSDavid du Colombier /* Recompute next_x for lines before the intersection. */
16637dd7cddfSDavid du Colombier for (alp = ll->x_list; alp != stopx; alp = alp->next)
16643ff48bf5SDavid du Colombier alp->x_next = AL_X_AT_Y(alp, y1);
1665*593dc095SDavid du Colombier /* Ensure X monotonity (particularly imporoves triple intersections). */
1666*593dc095SDavid du Colombier if (ll->x_list != NULL) {
1667*593dc095SDavid du Colombier for (;;) {
1668*593dc095SDavid du Colombier fixed x1;
1669*593dc095SDavid du Colombier double sx = 0xbaadf00d; /* 'fixed' can overflow. We operate 7-bytes integers. */
1670*593dc095SDavid du Colombier int k = 0xbaadf00d, n;
1671*593dc095SDavid du Colombier
1672*593dc095SDavid du Colombier endp = ll->x_list;
1673*593dc095SDavid du Colombier x1 = endp->x_next;
1674*593dc095SDavid du Colombier for (alp = endp->next; alp != NULL; x1 = alp->x_next, alp = alp->next)
1675*593dc095SDavid du Colombier if (alp->x_next < x1)
1676*593dc095SDavid du Colombier break;
1677*593dc095SDavid du Colombier if (alp == NULL)
1678*593dc095SDavid du Colombier break;
1679*593dc095SDavid du Colombier x1 = endp->x_next;
1680*593dc095SDavid du Colombier n = 0;
1681*593dc095SDavid du Colombier for (alp = endp->next; alp != NULL; alp = alp->next) {
1682*593dc095SDavid du Colombier x = alp->x_next;
1683*593dc095SDavid du Colombier if (x < x1) {
1684*593dc095SDavid du Colombier if (n == 0) {
1685*593dc095SDavid du Colombier if (endp->diff.x == 0) {
1686*593dc095SDavid du Colombier k = -1;
1687*593dc095SDavid du Colombier sx = x1;
1688*593dc095SDavid du Colombier } else {
1689*593dc095SDavid du Colombier k = coord_weight(endp);
1690*593dc095SDavid du Colombier sx = (double)x1 * k;
1691*593dc095SDavid du Colombier }
1692*593dc095SDavid du Colombier n = 1;
1693*593dc095SDavid du Colombier }
1694*593dc095SDavid du Colombier n++;
1695*593dc095SDavid du Colombier if (alp->diff.x == 0) {
1696*593dc095SDavid du Colombier /* Vertical lines have a higher priority. */
1697*593dc095SDavid du Colombier if (k <= -1) {
1698*593dc095SDavid du Colombier sx += x;
1699*593dc095SDavid du Colombier k--;
1700*593dc095SDavid du Colombier } else {
1701*593dc095SDavid du Colombier k = -1;
1702*593dc095SDavid du Colombier sx = x;
1703*593dc095SDavid du Colombier }
1704*593dc095SDavid du Colombier } else if (k > 0) {
1705*593dc095SDavid du Colombier int w = coord_weight(alp);
1706*593dc095SDavid du Colombier
1707*593dc095SDavid du Colombier sx += (double)x * w;
1708*593dc095SDavid du Colombier k += w;
1709*593dc095SDavid du Colombier }
1710*593dc095SDavid du Colombier } else {
1711*593dc095SDavid du Colombier if (n > 1) {
1712*593dc095SDavid du Colombier k = any_abs(k);
1713*593dc095SDavid du Colombier set_x_next(endp, alp, (fixed)((sx + k / 2) / k));
1714*593dc095SDavid du Colombier }
1715*593dc095SDavid du Colombier x1 = alp->x_next;
1716*593dc095SDavid du Colombier n = 0;
1717*593dc095SDavid du Colombier endp = alp;
1718*593dc095SDavid du Colombier }
1719*593dc095SDavid du Colombier }
1720*593dc095SDavid du Colombier if (n > 1) {
1721*593dc095SDavid du Colombier k = any_abs(k);
1722*593dc095SDavid du Colombier set_x_next(endp, alp, (fixed)((sx + k / 2) / k));
1723*593dc095SDavid du Colombier }
1724*593dc095SDavid du Colombier }
1725*593dc095SDavid du Colombier }
1726*593dc095SDavid du Colombier } else {
1727*593dc095SDavid du Colombier /* Recompute next_x for lines before the intersection. */
1728*593dc095SDavid du Colombier for (alp = ll->x_list; alp != stopx; alp = alp->next)
1729*593dc095SDavid du Colombier alp->x_next = AL_X_AT_Y(alp, y1);
1730*593dc095SDavid du Colombier }
17317dd7cddfSDavid du Colombier #ifdef DEBUG
17327dd7cddfSDavid du Colombier if (gs_debug_c('F')) {
17337dd7cddfSDavid du Colombier dlprintf1("[F]after loop: y1=%f\n", fixed2float(y1));
17347dd7cddfSDavid du Colombier print_line_list(ll->x_list);
17357dd7cddfSDavid du Colombier }
17367dd7cddfSDavid du Colombier #endif
1737*593dc095SDavid du Colombier *y_top = y1;
17387dd7cddfSDavid du Colombier }
17397dd7cddfSDavid du Colombier
1740*593dc095SDavid du Colombier /* ---------------- Trapezoid filling loop ---------------- */
1741*593dc095SDavid du Colombier
1742*593dc095SDavid du Colombier /* Generate specialized algorythms for the most important cases : */
1743*593dc095SDavid du Colombier
1744*593dc095SDavid du Colombier #define IS_SPOTAN 1
1745*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 0
1746*593dc095SDavid du Colombier #define FILL_ADJUST 0
1747*593dc095SDavid du Colombier #define FILL_DIRECT 1
1748*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__spotan
1749*593dc095SDavid du Colombier #include "gxfilltr.h"
1750*593dc095SDavid du Colombier #undef IS_SPOTAN
1751*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1752*593dc095SDavid du Colombier #undef FILL_ADJUST
1753*593dc095SDavid du Colombier #undef FILL_DIRECT
1754*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1755*593dc095SDavid du Colombier
1756*593dc095SDavid du Colombier #define IS_SPOTAN 0
1757*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 1
1758*593dc095SDavid du Colombier #define FILL_ADJUST 0
1759*593dc095SDavid du Colombier #define FILL_DIRECT 1
1760*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__pr_fd
1761*593dc095SDavid du Colombier #include "gxfilltr.h"
1762*593dc095SDavid du Colombier #undef IS_SPOTAN
1763*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1764*593dc095SDavid du Colombier #undef FILL_ADJUST
1765*593dc095SDavid du Colombier #undef FILL_DIRECT
1766*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1767*593dc095SDavid du Colombier
1768*593dc095SDavid du Colombier #define IS_SPOTAN 0
1769*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 1
1770*593dc095SDavid du Colombier #define FILL_ADJUST 0
1771*593dc095SDavid du Colombier #define FILL_DIRECT 0
1772*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__pr_nd
1773*593dc095SDavid du Colombier #include "gxfilltr.h"
1774*593dc095SDavid du Colombier #undef IS_SPOTAN
1775*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1776*593dc095SDavid du Colombier #undef FILL_ADJUST
1777*593dc095SDavid du Colombier #undef FILL_DIRECT
1778*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1779*593dc095SDavid du Colombier
1780*593dc095SDavid du Colombier #define IS_SPOTAN 0
1781*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 0
1782*593dc095SDavid du Colombier #define FILL_ADJUST 1
1783*593dc095SDavid du Colombier #define FILL_DIRECT 1
1784*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__aj_fd
1785*593dc095SDavid du Colombier #include "gxfilltr.h"
1786*593dc095SDavid du Colombier #undef IS_SPOTAN
1787*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1788*593dc095SDavid du Colombier #undef FILL_ADJUST
1789*593dc095SDavid du Colombier #undef FILL_DIRECT
1790*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1791*593dc095SDavid du Colombier
1792*593dc095SDavid du Colombier #define IS_SPOTAN 0
1793*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 0
1794*593dc095SDavid du Colombier #define FILL_ADJUST 1
1795*593dc095SDavid du Colombier #define FILL_DIRECT 0
1796*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__aj_nd
1797*593dc095SDavid du Colombier #include "gxfilltr.h"
1798*593dc095SDavid du Colombier #undef IS_SPOTAN
1799*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1800*593dc095SDavid du Colombier #undef FILL_ADJUST
1801*593dc095SDavid du Colombier #undef FILL_DIRECT
1802*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1803*593dc095SDavid du Colombier
1804*593dc095SDavid du Colombier #define IS_SPOTAN 0
1805*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 0
1806*593dc095SDavid du Colombier #define FILL_ADJUST 0
1807*593dc095SDavid du Colombier #define FILL_DIRECT 1
1808*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_fd
1809*593dc095SDavid du Colombier #include "gxfilltr.h"
1810*593dc095SDavid du Colombier #undef IS_SPOTAN
1811*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1812*593dc095SDavid du Colombier #undef FILL_ADJUST
1813*593dc095SDavid du Colombier #undef FILL_DIRECT
1814*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1815*593dc095SDavid du Colombier
1816*593dc095SDavid du Colombier #define IS_SPOTAN 0
1817*593dc095SDavid du Colombier #define PSEUDO_RASTERIZATION 0
1818*593dc095SDavid du Colombier #define FILL_ADJUST 0
1819*593dc095SDavid du Colombier #define FILL_DIRECT 0
1820*593dc095SDavid du Colombier #define TEMPLATE_spot_into_trapezoids spot_into_trapezoids__nj_nd
1821*593dc095SDavid du Colombier #include "gxfilltr.h"
1822*593dc095SDavid du Colombier #undef IS_SPOTAN
1823*593dc095SDavid du Colombier #undef PSEUDO_RASTERIZATION
1824*593dc095SDavid du Colombier #undef FILL_ADJUST
1825*593dc095SDavid du Colombier #undef FILL_DIRECT
1826*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_trapezoids
1827*593dc095SDavid du Colombier
1828*593dc095SDavid du Colombier
1829*593dc095SDavid du Colombier /* Main filling loop. Takes lines off of y_list and adds them to */
1830*593dc095SDavid du Colombier /* x_list as needed. band_mask limits the size of each band, */
1831*593dc095SDavid du Colombier /* by requiring that ((y1 - 1) & band_mask) == (y0 & band_mask). */
18327dd7cddfSDavid du Colombier private int
spot_into_trapezoids(line_list * ll,fixed band_mask)1833*593dc095SDavid du Colombier spot_into_trapezoids(line_list *ll, fixed band_mask)
18347dd7cddfSDavid du Colombier {
1835*593dc095SDavid du Colombier /* For better porformance, choose a specialized algorithm : */
1836*593dc095SDavid du Colombier const fill_options * const fo = ll->fo;
18377dd7cddfSDavid du Colombier
1838*593dc095SDavid du Colombier if (fo->is_spotan)
1839*593dc095SDavid du Colombier return spot_into_trapezoids__spotan(ll, band_mask);
1840*593dc095SDavid du Colombier if (fo->pseudo_rasterization) {
1841*593dc095SDavid du Colombier if (fo->fill_direct)
1842*593dc095SDavid du Colombier return spot_into_trapezoids__pr_fd(ll, band_mask);
18437dd7cddfSDavid du Colombier else
1844*593dc095SDavid du Colombier return spot_into_trapezoids__pr_nd(ll, band_mask);
1845*593dc095SDavid du Colombier }
1846*593dc095SDavid du Colombier if (fo->adjust_below | fo->adjust_above | fo->adjust_left | fo->adjust_right) {
1847*593dc095SDavid du Colombier if (fo->fill_direct)
1848*593dc095SDavid du Colombier return spot_into_trapezoids__aj_fd(ll, band_mask);
1849*593dc095SDavid du Colombier else
1850*593dc095SDavid du Colombier return spot_into_trapezoids__aj_nd(ll, band_mask);
18517dd7cddfSDavid du Colombier } else {
1852*593dc095SDavid du Colombier if (fo->fill_direct)
1853*593dc095SDavid du Colombier return spot_into_trapezoids__nj_fd(ll, band_mask);
1854*593dc095SDavid du Colombier else
1855*593dc095SDavid du Colombier return spot_into_trapezoids__nj_nd(ll, band_mask);
18567dd7cddfSDavid du Colombier }
18577dd7cddfSDavid du Colombier }
18583ff48bf5SDavid du Colombier
18593ff48bf5SDavid du Colombier /* ---------------- Range list management ---------------- */
18603ff48bf5SDavid du Colombier
18613ff48bf5SDavid du Colombier /*
18623ff48bf5SDavid du Colombier * We originally thought we would store fixed values in coordinate ranges,
18633ff48bf5SDavid du Colombier * but it turned out we want to store integers.
18643ff48bf5SDavid du Colombier */
18653ff48bf5SDavid du Colombier typedef int coord_value_t;
18663ff48bf5SDavid du Colombier #define MIN_COORD_VALUE min_int
18673ff48bf5SDavid du Colombier #define MAX_COORD_VALUE max_int
18683ff48bf5SDavid du Colombier /*
18693ff48bf5SDavid du Colombier * The invariant for coord_range_ts in a coord_range_list_t:
18703ff48bf5SDavid du Colombier * if prev == 0:
18713ff48bf5SDavid du Colombier * next != 0
18723ff48bf5SDavid du Colombier * rmin == rmax == MIN_COORD_VALUE
18733ff48bf5SDavid du Colombier * else if next == 0:
18743ff48bf5SDavid du Colombier * prev != 0
18753ff48bf5SDavid du Colombier * rmin == rmax == MAX_COORD_VALUE
18763ff48bf5SDavid du Colombier * else
18773ff48bf5SDavid du Colombier * rmin < rmax
18783ff48bf5SDavid du Colombier * if prev != 0:
18793ff48bf5SDavid du Colombier * prev->next == this
18803ff48bf5SDavid du Colombier * prev->rmax < rmin
18813ff48bf5SDavid du Colombier * if next != 0:
18823ff48bf5SDavid du Colombier * next->prev = this
18833ff48bf5SDavid du Colombier * next->rmin > rmax
18843ff48bf5SDavid du Colombier * A coord_range_list_t has a boundary element at each end to avoid having
18853ff48bf5SDavid du Colombier * to test for null pointers in the insertion loop. The boundary elements
18863ff48bf5SDavid du Colombier * are the only empty ranges.
18873ff48bf5SDavid du Colombier */
18883ff48bf5SDavid du Colombier typedef struct coord_range_s coord_range_t;
18893ff48bf5SDavid du Colombier struct coord_range_s {
18903ff48bf5SDavid du Colombier coord_value_t rmin, rmax;
18913ff48bf5SDavid du Colombier coord_range_t *prev, *next;
18923ff48bf5SDavid du Colombier coord_range_t *alloc_next;
18933ff48bf5SDavid du Colombier };
18943ff48bf5SDavid du Colombier /* We declare coord_range_ts as simple because they only exist transiently. */
18953ff48bf5SDavid du Colombier gs_private_st_simple(st_coord_range, coord_range_t, "coord_range_t");
18963ff48bf5SDavid du Colombier
18973ff48bf5SDavid du Colombier typedef struct coord_range_list_s {
18983ff48bf5SDavid du Colombier gs_memory_t *memory;
18993ff48bf5SDavid du Colombier struct rl_ {
19003ff48bf5SDavid du Colombier coord_range_t *first, *next, *limit;
19013ff48bf5SDavid du Colombier } local;
19023ff48bf5SDavid du Colombier coord_range_t *allocated;
19033ff48bf5SDavid du Colombier coord_range_t *freed;
19043ff48bf5SDavid du Colombier coord_range_t *current;
19053ff48bf5SDavid du Colombier coord_range_t first, last;
19063ff48bf5SDavid du Colombier } coord_range_list_t;
19073ff48bf5SDavid du Colombier
19083ff48bf5SDavid du Colombier private coord_range_t *
range_alloc(coord_range_list_t * pcrl)19093ff48bf5SDavid du Colombier range_alloc(coord_range_list_t *pcrl)
19103ff48bf5SDavid du Colombier {
19113ff48bf5SDavid du Colombier coord_range_t *pcr;
19123ff48bf5SDavid du Colombier
19133ff48bf5SDavid du Colombier if (pcrl->freed) {
19143ff48bf5SDavid du Colombier pcr = pcrl->freed;
19153ff48bf5SDavid du Colombier pcrl->freed = pcr->next;
19163ff48bf5SDavid du Colombier } else if (pcrl->local.next < pcrl->local.limit)
19173ff48bf5SDavid du Colombier pcr = pcrl->local.next++;
19183ff48bf5SDavid du Colombier else {
19193ff48bf5SDavid du Colombier pcr = gs_alloc_struct(pcrl->memory, coord_range_t, &st_coord_range,
19203ff48bf5SDavid du Colombier "range_alloc");
19213ff48bf5SDavid du Colombier if (pcr == 0)
19223ff48bf5SDavid du Colombier return 0;
19233ff48bf5SDavid du Colombier pcr->alloc_next = pcrl->allocated;
19243ff48bf5SDavid du Colombier pcrl->allocated = pcr;
19253ff48bf5SDavid du Colombier }
19263ff48bf5SDavid du Colombier return pcr;
19273ff48bf5SDavid du Colombier }
19283ff48bf5SDavid du Colombier
19293ff48bf5SDavid du Colombier private void
range_delete(coord_range_list_t * pcrl,coord_range_t * pcr)19303ff48bf5SDavid du Colombier range_delete(coord_range_list_t *pcrl, coord_range_t *pcr)
19313ff48bf5SDavid du Colombier {
19323ff48bf5SDavid du Colombier if_debug3('Q', "[Qr]delete 0x%lx: [%d,%d)\n", (ulong)pcr, pcr->rmin,
19333ff48bf5SDavid du Colombier pcr->rmax);
19343ff48bf5SDavid du Colombier pcr->prev->next = pcr->next;
19353ff48bf5SDavid du Colombier pcr->next->prev = pcr->prev;
19363ff48bf5SDavid du Colombier pcr->next = pcrl->freed;
19373ff48bf5SDavid du Colombier pcrl->freed = pcr;
19383ff48bf5SDavid du Colombier }
19393ff48bf5SDavid du Colombier
19403ff48bf5SDavid du Colombier private void
range_list_clear(coord_range_list_t * pcrl)19413ff48bf5SDavid du Colombier range_list_clear(coord_range_list_t *pcrl)
19423ff48bf5SDavid du Colombier {
19433ff48bf5SDavid du Colombier if_debug0('Q', "[Qr]clearing\n");
19443ff48bf5SDavid du Colombier pcrl->first.next = &pcrl->last;
19453ff48bf5SDavid du Colombier pcrl->last.prev = &pcrl->first;
19463ff48bf5SDavid du Colombier pcrl->current = &pcrl->last;
19473ff48bf5SDavid du Colombier }
19483ff48bf5SDavid du Colombier
19493ff48bf5SDavid du Colombier /* ------ "Public" procedures ------ */
19503ff48bf5SDavid du Colombier
19513ff48bf5SDavid du Colombier /* Initialize a range list. We require num_local >= 2. */
1952*593dc095SDavid du Colombier private void range_list_clear(coord_range_list_t *);
19533ff48bf5SDavid du Colombier private void
range_list_init(coord_range_list_t * pcrl,coord_range_t * pcr_local,int num_local,gs_memory_t * mem)19543ff48bf5SDavid du Colombier range_list_init(coord_range_list_t *pcrl, coord_range_t *pcr_local,
19553ff48bf5SDavid du Colombier int num_local, gs_memory_t *mem)
19563ff48bf5SDavid du Colombier {
19573ff48bf5SDavid du Colombier pcrl->memory = mem;
19583ff48bf5SDavid du Colombier pcrl->local.first = pcrl->local.next = pcr_local;
19593ff48bf5SDavid du Colombier pcrl->local.limit = pcr_local + num_local;
19603ff48bf5SDavid du Colombier pcrl->allocated = pcrl->freed = 0;
19613ff48bf5SDavid du Colombier pcrl->first.rmin = pcrl->first.rmax = MIN_COORD_VALUE;
19623ff48bf5SDavid du Colombier pcrl->first.prev = 0;
19633ff48bf5SDavid du Colombier pcrl->last.rmin = pcrl->last.rmax = MAX_COORD_VALUE;
19643ff48bf5SDavid du Colombier pcrl->last.next = 0;
19653ff48bf5SDavid du Colombier range_list_clear(pcrl);
19663ff48bf5SDavid du Colombier }
19673ff48bf5SDavid du Colombier
19683ff48bf5SDavid du Colombier /* Reset an initialized range list to the empty state. */
19693ff48bf5SDavid du Colombier private void
range_list_reset(coord_range_list_t * pcrl)19703ff48bf5SDavid du Colombier range_list_reset(coord_range_list_t *pcrl)
19713ff48bf5SDavid du Colombier {
19723ff48bf5SDavid du Colombier if (pcrl->first.next != &pcrl->last) {
19733ff48bf5SDavid du Colombier pcrl->last.prev->next = pcrl->freed;
19743ff48bf5SDavid du Colombier pcrl->freed = pcrl->first.next;
19753ff48bf5SDavid du Colombier range_list_clear(pcrl);
19763ff48bf5SDavid du Colombier }
19773ff48bf5SDavid du Colombier }
19783ff48bf5SDavid du Colombier
19793ff48bf5SDavid du Colombier /*
19803ff48bf5SDavid du Colombier * Move the cursor to the beginning of a range list, in the belief that
19813ff48bf5SDavid du Colombier * the next added ranges will roughly parallel the existing ones.
19823ff48bf5SDavid du Colombier * (Only affects performance, not function.)
19833ff48bf5SDavid du Colombier */
19843ff48bf5SDavid du Colombier inline private void
range_list_rescan(coord_range_list_t * pcrl)19853ff48bf5SDavid du Colombier range_list_rescan(coord_range_list_t *pcrl)
19863ff48bf5SDavid du Colombier {
19873ff48bf5SDavid du Colombier pcrl->current = pcrl->first.next;
19883ff48bf5SDavid du Colombier }
19893ff48bf5SDavid du Colombier
19903ff48bf5SDavid du Colombier /* Free a range list. */
19913ff48bf5SDavid du Colombier private void
range_list_free(coord_range_list_t * pcrl)19923ff48bf5SDavid du Colombier range_list_free(coord_range_list_t *pcrl)
19933ff48bf5SDavid du Colombier {
19943ff48bf5SDavid du Colombier coord_range_t *pcr;
19953ff48bf5SDavid du Colombier
19963ff48bf5SDavid du Colombier while ((pcr = pcrl->allocated) != 0) {
19973ff48bf5SDavid du Colombier coord_range_t *next = pcr->alloc_next;
19983ff48bf5SDavid du Colombier
19993ff48bf5SDavid du Colombier gs_free_object(pcrl->memory, pcr, "range_list_free");
20003ff48bf5SDavid du Colombier pcrl->allocated = next;
20013ff48bf5SDavid du Colombier }
20023ff48bf5SDavid du Colombier }
20033ff48bf5SDavid du Colombier
20043ff48bf5SDavid du Colombier /* Add a range. */
20053ff48bf5SDavid du Colombier private int
range_list_add(coord_range_list_t * pcrl,coord_value_t rmin,coord_value_t rmax)20063ff48bf5SDavid du Colombier range_list_add(coord_range_list_t *pcrl, coord_value_t rmin, coord_value_t rmax)
20073ff48bf5SDavid du Colombier {
20083ff48bf5SDavid du Colombier coord_range_t *pcr = pcrl->current;
20093ff48bf5SDavid du Colombier
20103ff48bf5SDavid du Colombier if (rmin >= rmax)
20113ff48bf5SDavid du Colombier return 0;
20123ff48bf5SDavid du Colombier /*
20133ff48bf5SDavid du Colombier * Usually, ranges are added in increasing order within each scan line,
20143ff48bf5SDavid du Colombier * and overlapping ranges don't differ much. Optimize accordingly.
20153ff48bf5SDavid du Colombier */
20163ff48bf5SDavid du Colombier top:
20173ff48bf5SDavid du Colombier if (rmax < pcr->rmin) {
20183ff48bf5SDavid du Colombier if (rmin > pcr->prev->rmax)
20193ff48bf5SDavid du Colombier goto ins;
20203ff48bf5SDavid du Colombier pcr = pcr->prev;
20213ff48bf5SDavid du Colombier goto top;
20223ff48bf5SDavid du Colombier }
20233ff48bf5SDavid du Colombier if (rmin > pcr->rmax) {
20243ff48bf5SDavid du Colombier pcr = pcr->next;
20253ff48bf5SDavid du Colombier if (rmax < pcr->rmin)
20263ff48bf5SDavid du Colombier goto ins;
20273ff48bf5SDavid du Colombier goto top;
20283ff48bf5SDavid du Colombier }
20293ff48bf5SDavid du Colombier /*
20303ff48bf5SDavid du Colombier * Now we know that (rmin,rmax) overlaps (pcr->rmin,pcr->rmax).
20313ff48bf5SDavid du Colombier * Don't delete or merge into the special min and max ranges.
20323ff48bf5SDavid du Colombier */
20333ff48bf5SDavid du Colombier while (rmin <= pcr->prev->rmax) {
20343ff48bf5SDavid du Colombier /* Merge the range below pcr into this one. */
20353ff48bf5SDavid du Colombier if (!pcr->prev->prev)
20363ff48bf5SDavid du Colombier break; /* don't merge into the min range */
20373ff48bf5SDavid du Colombier pcr->rmin = pcr->prev->rmin;
20383ff48bf5SDavid du Colombier range_delete(pcrl, pcr->prev);
20393ff48bf5SDavid du Colombier }
20403ff48bf5SDavid du Colombier while (rmax >= pcr->next->rmin) {
20413ff48bf5SDavid du Colombier /* Merge the range above pcr into this one. */
20423ff48bf5SDavid du Colombier if (!pcr->next->next)
20433ff48bf5SDavid du Colombier break; /* don't merge into the max range */
20443ff48bf5SDavid du Colombier pcr->rmax = pcr->next->rmax;
20453ff48bf5SDavid du Colombier range_delete(pcrl, pcr->next);
20463ff48bf5SDavid du Colombier }
20473ff48bf5SDavid du Colombier /*
20483ff48bf5SDavid du Colombier * Now we know that the union of (rmin,rmax) and (pcr->rmin,pcr->rmax)
20493ff48bf5SDavid du Colombier * doesn't overlap or abut either adjacent range, except that it may
20503ff48bf5SDavid du Colombier * abut if the adjacent range is the special min or max range.
20513ff48bf5SDavid du Colombier */
20523ff48bf5SDavid du Colombier if (rmin < pcr->rmin) {
20533ff48bf5SDavid du Colombier if_debug3('Q', "[Qr]update 0x%lx => [%d,%d)\n", (ulong)pcr, rmin,
20543ff48bf5SDavid du Colombier pcr->rmax);
20553ff48bf5SDavid du Colombier pcr->rmin = rmin;
20563ff48bf5SDavid du Colombier }
20573ff48bf5SDavid du Colombier if (rmax > pcr->rmax) {
20583ff48bf5SDavid du Colombier if_debug3('Q', "[Qr]update 0x%lx => [%d,%d)\n", (ulong)pcr, pcr->rmin,
20593ff48bf5SDavid du Colombier rmax);
20603ff48bf5SDavid du Colombier pcr->rmax = rmax;
20613ff48bf5SDavid du Colombier }
20623ff48bf5SDavid du Colombier pcrl->current = pcr->next;
20633ff48bf5SDavid du Colombier return 0;
20643ff48bf5SDavid du Colombier ins:
20653ff48bf5SDavid du Colombier /* Insert a new range below pcr. */
20663ff48bf5SDavid du Colombier {
20673ff48bf5SDavid du Colombier coord_range_t *prev = range_alloc(pcrl);
20683ff48bf5SDavid du Colombier
20693ff48bf5SDavid du Colombier if (prev == 0)
20703ff48bf5SDavid du Colombier return_error(gs_error_VMerror);
20713ff48bf5SDavid du Colombier if_debug3('Q', "[Qr]insert 0x%lx: [%d,%d)\n", (ulong)prev, rmin, rmax);
20723ff48bf5SDavid du Colombier prev->rmin = rmin, prev->rmax = rmax;
20733ff48bf5SDavid du Colombier (prev->prev = pcr->prev)->next = prev;
20743ff48bf5SDavid du Colombier prev->next = pcr;
20753ff48bf5SDavid du Colombier pcr->prev = prev;
20763ff48bf5SDavid du Colombier }
20773ff48bf5SDavid du Colombier pcrl->current = pcr;
20783ff48bf5SDavid du Colombier return 0;
20793ff48bf5SDavid du Colombier }
20803ff48bf5SDavid du Colombier
20813ff48bf5SDavid du Colombier /*
20823ff48bf5SDavid du Colombier * Merge regions for active segments starting at a given Y, or all active
20833ff48bf5SDavid du Colombier * segments, up to the end of the sampling band or the end of the segment,
20843ff48bf5SDavid du Colombier * into the range list.
20853ff48bf5SDavid du Colombier */
20863ff48bf5SDavid du Colombier private int
merge_ranges(coord_range_list_t * pcrl,const line_list * ll,fixed y_min,fixed y_top)2087*593dc095SDavid du Colombier merge_ranges(coord_range_list_t *pcrl, const line_list *ll, fixed y_min, fixed y_top)
20883ff48bf5SDavid du Colombier {
20893ff48bf5SDavid du Colombier active_line *alp, *nlp;
20903ff48bf5SDavid du Colombier int code = 0;
20913ff48bf5SDavid du Colombier
20923ff48bf5SDavid du Colombier range_list_rescan(pcrl);
20933ff48bf5SDavid du Colombier for (alp = ll->x_list; alp != 0 && code >= 0; alp = nlp) {
20943ff48bf5SDavid du Colombier fixed x0 = alp->x_current, x1, xt;
20953ff48bf5SDavid du Colombier
20963ff48bf5SDavid du Colombier nlp = alp->next;
20973ff48bf5SDavid du Colombier if (alp->start.y < y_min)
20983ff48bf5SDavid du Colombier continue;
2099*593dc095SDavid du Colombier if (alp->monotonic_x && alp->monotonic_y && alp->fi.y3 <= y_top) {
2100*593dc095SDavid du Colombier vd_bar(alp->start.x, alp->start.y, alp->end.x, alp->end.y, 0, RGB(255, 0, 0));
2101*593dc095SDavid du Colombier x1 = alp->fi.x3;
21023ff48bf5SDavid du Colombier if (x0 > x1)
21033ff48bf5SDavid du Colombier xt = x0, x0 = x1, x1 = xt;
21043ff48bf5SDavid du Colombier code = range_list_add(pcrl,
2105*593dc095SDavid du Colombier fixed2int_pixround(x0 - ll->fo->adjust_left),
2106*593dc095SDavid du Colombier fixed2int_rounded(x1 + ll->fo->adjust_right));
2107*593dc095SDavid du Colombier alp->more_flattened = false; /* Skip all the segments left. */
2108*593dc095SDavid du Colombier } else {
2109*593dc095SDavid du Colombier x1 = x0;
2110*593dc095SDavid du Colombier for (;;) {
2111*593dc095SDavid du Colombier if (alp->end.y <= y_top)
2112*593dc095SDavid du Colombier xt = alp->end.x;
2113*593dc095SDavid du Colombier else
2114*593dc095SDavid du Colombier xt = AL_X_AT_Y(alp, y_top);
2115*593dc095SDavid du Colombier x0 = min(x0, xt);
2116*593dc095SDavid du Colombier x1 = max(x1, xt);
2117*593dc095SDavid du Colombier if (!alp->more_flattened || alp->end.y > y_top)
2118*593dc095SDavid du Colombier break;
2119*593dc095SDavid du Colombier step_al(alp, true);
2120*593dc095SDavid du Colombier if (alp->end.y < alp->start.y) {
2121*593dc095SDavid du Colombier remove_al(ll, alp); /* End of a monotonic part of a curve. */
2122*593dc095SDavid du Colombier break;
2123*593dc095SDavid du Colombier }
2124*593dc095SDavid du Colombier }
2125*593dc095SDavid du Colombier code = range_list_add(pcrl,
2126*593dc095SDavid du Colombier fixed2int_pixround(x0 - ll->fo->adjust_left),
2127*593dc095SDavid du Colombier fixed2int_rounded(x1 + ll->fo->adjust_right));
2128*593dc095SDavid du Colombier }
2129*593dc095SDavid du Colombier
21303ff48bf5SDavid du Colombier }
21313ff48bf5SDavid du Colombier return code;
21323ff48bf5SDavid du Colombier }
21333ff48bf5SDavid du Colombier
2134*593dc095SDavid du Colombier /* ---------------- Scan line filling loop ---------------- */
21353ff48bf5SDavid du Colombier
2136*593dc095SDavid du Colombier /* defina specializations of the scanline algorithm. */
21373ff48bf5SDavid du Colombier
2138*593dc095SDavid du Colombier #define FILL_DIRECT 1
2139*593dc095SDavid du Colombier #define TEMPLATE_spot_into_scanlines spot_into_scan_lines_fd
2140*593dc095SDavid du Colombier #include "gxfillsl.h"
2141*593dc095SDavid du Colombier #undef FILL_DIRECT
2142*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_scanlines
2143*593dc095SDavid du Colombier
2144*593dc095SDavid du Colombier #define FILL_DIRECT 0
2145*593dc095SDavid du Colombier #define TEMPLATE_spot_into_scanlines spot_into_scan_lines_nd
2146*593dc095SDavid du Colombier #include "gxfillsl.h"
2147*593dc095SDavid du Colombier #undef FILL_DIRECT
2148*593dc095SDavid du Colombier #undef TEMPLATE_spot_into_scanlines
2149*593dc095SDavid du Colombier
2150*593dc095SDavid du Colombier private int
spot_into_scan_lines(line_list * ll,fixed band_mask)2151*593dc095SDavid du Colombier spot_into_scan_lines(line_list *ll, fixed band_mask)
2152*593dc095SDavid du Colombier {
2153*593dc095SDavid du Colombier if (ll->fo->fill_direct)
2154*593dc095SDavid du Colombier return spot_into_scan_lines_fd(ll, band_mask);
2155*593dc095SDavid du Colombier else
2156*593dc095SDavid du Colombier return spot_into_scan_lines_nd(ll, band_mask);
21573ff48bf5SDavid du Colombier }
2158*593dc095SDavid du Colombier
2159