xref: /plan9-contrib/sys/src/cmd/gs/src/gxfill.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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