xref: /plan9-contrib/sys/src/cmd/gs/src/gxacpath.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
13ff48bf5SDavid du Colombier /* Copyright (C) 1993, 2000 Aladdin Enterprises.  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: gxacpath.c,v 1.10 2004/08/04 19:36:12 stefan Exp $ */
187dd7cddfSDavid du Colombier /* Accumulator for clipping paths */
197dd7cddfSDavid du Colombier #include "gx.h"
207dd7cddfSDavid du Colombier #include "gserrors.h"
217dd7cddfSDavid du Colombier #include "gsrop.h"
227dd7cddfSDavid du Colombier #include "gsstruct.h"
237dd7cddfSDavid du Colombier #include "gsutil.h"
247dd7cddfSDavid du Colombier #include "gsdcolor.h"
257dd7cddfSDavid du Colombier #include "gxdevice.h"
267dd7cddfSDavid du Colombier #include "gxfixed.h"
277dd7cddfSDavid du Colombier #include "gxistate.h"
287dd7cddfSDavid du Colombier #include "gzpath.h"
297dd7cddfSDavid du Colombier #include "gxpaint.h"
307dd7cddfSDavid du Colombier #include "gzcpath.h"
317dd7cddfSDavid du Colombier #include "gzacpath.h"
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier /* Device procedures */
347dd7cddfSDavid du Colombier private dev_proc_open_device(accum_open);
357dd7cddfSDavid du Colombier private dev_proc_close_device(accum_close);
367dd7cddfSDavid du Colombier private dev_proc_fill_rectangle(accum_fill_rectangle);
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier /* The device descriptor */
397dd7cddfSDavid du Colombier /* Many of these procedures won't be called; they are set to NULL. */
407dd7cddfSDavid du Colombier private const gx_device_cpath_accum gs_cpath_accum_device =
417dd7cddfSDavid du Colombier {std_device_std_body(gx_device_cpath_accum, 0, "clip list accumulator",
427dd7cddfSDavid du Colombier 		     0, 0, 1, 1),
437dd7cddfSDavid du Colombier  {accum_open,
447dd7cddfSDavid du Colombier   NULL,
457dd7cddfSDavid du Colombier   NULL,
467dd7cddfSDavid du Colombier   NULL,
477dd7cddfSDavid du Colombier   accum_close,
487dd7cddfSDavid du Colombier   NULL,
497dd7cddfSDavid du Colombier   NULL,
507dd7cddfSDavid du Colombier   accum_fill_rectangle,
517dd7cddfSDavid du Colombier   NULL,
527dd7cddfSDavid du Colombier   NULL,
537dd7cddfSDavid du Colombier   NULL,
547dd7cddfSDavid du Colombier   NULL,
557dd7cddfSDavid du Colombier   NULL,
567dd7cddfSDavid du Colombier   NULL,
577dd7cddfSDavid du Colombier   NULL,
587dd7cddfSDavid du Colombier   NULL,
597dd7cddfSDavid du Colombier   NULL,
607dd7cddfSDavid du Colombier   NULL,
617dd7cddfSDavid du Colombier   NULL,
627dd7cddfSDavid du Colombier   NULL,
637dd7cddfSDavid du Colombier   NULL,
647dd7cddfSDavid du Colombier   NULL,
657dd7cddfSDavid du Colombier   NULL,
667dd7cddfSDavid du Colombier   NULL,
677dd7cddfSDavid du Colombier   gx_default_fill_path,
687dd7cddfSDavid du Colombier   gx_default_stroke_path,
697dd7cddfSDavid du Colombier   NULL,
707dd7cddfSDavid du Colombier   gx_default_fill_trapezoid,
717dd7cddfSDavid du Colombier   gx_default_fill_parallelogram,
727dd7cddfSDavid du Colombier   gx_default_fill_triangle,
737dd7cddfSDavid du Colombier   gx_default_draw_thin_line,
747dd7cddfSDavid du Colombier   gx_default_begin_image,
757dd7cddfSDavid du Colombier   gx_default_image_data,
767dd7cddfSDavid du Colombier   gx_default_end_image,
777dd7cddfSDavid du Colombier   NULL,
787dd7cddfSDavid du Colombier   NULL,
797dd7cddfSDavid du Colombier   gx_get_largest_clipping_box,
807dd7cddfSDavid du Colombier   gx_default_begin_typed_image,
817dd7cddfSDavid du Colombier   NULL,
827dd7cddfSDavid du Colombier   NULL,
837dd7cddfSDavid du Colombier   NULL,
847dd7cddfSDavid du Colombier   NULL,
853ff48bf5SDavid du Colombier   gx_default_text_begin,
86*593dc095SDavid du Colombier   gx_default_finish_copydevice,
87*593dc095SDavid du Colombier   NULL,
88*593dc095SDavid du Colombier   NULL,
89*593dc095SDavid du Colombier   NULL,
90*593dc095SDavid du Colombier   NULL,
91*593dc095SDavid du Colombier   NULL,
92*593dc095SDavid du Colombier   NULL,
93*593dc095SDavid du Colombier   NULL,
94*593dc095SDavid du Colombier   NULL,
95*593dc095SDavid du Colombier   NULL
967dd7cddfSDavid du Colombier  }
977dd7cddfSDavid du Colombier };
987dd7cddfSDavid du Colombier 
997dd7cddfSDavid du Colombier /* Start accumulating a clipping path. */
1007dd7cddfSDavid du Colombier void
gx_cpath_accum_begin(gx_device_cpath_accum * padev,gs_memory_t * mem)1017dd7cddfSDavid du Colombier gx_cpath_accum_begin(gx_device_cpath_accum * padev, gs_memory_t * mem)
1027dd7cddfSDavid du Colombier {
1037dd7cddfSDavid du Colombier     gx_device_init((gx_device *) padev,
1047dd7cddfSDavid du Colombier 		   (const gx_device *) & gs_cpath_accum_device,
1057dd7cddfSDavid du Colombier 		   NULL /* allocated on stack */ , true);
1067dd7cddfSDavid du Colombier     padev->list_memory = mem;
1077dd7cddfSDavid du Colombier     (*dev_proc(padev, open_device)) ((gx_device *) padev);
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier void
gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,const gs_fixed_rect * pbox)1117dd7cddfSDavid du Colombier gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,
1127dd7cddfSDavid du Colombier 			const gs_fixed_rect * pbox)
1137dd7cddfSDavid du Colombier {
1147dd7cddfSDavid du Colombier     padev->clip_box.p.x = fixed2int_var(pbox->p.x);
1157dd7cddfSDavid du Colombier     padev->clip_box.p.y = fixed2int_var(pbox->p.y);
1167dd7cddfSDavid du Colombier     padev->clip_box.q.x = fixed2int_var_ceiling(pbox->q.x);
1177dd7cddfSDavid du Colombier     padev->clip_box.q.y = fixed2int_var_ceiling(pbox->q.y);
1187dd7cddfSDavid du Colombier }
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier /* Finish accumulating a clipping path. */
1217dd7cddfSDavid du Colombier int
gx_cpath_accum_end(const gx_device_cpath_accum * padev,gx_clip_path * pcpath)1227dd7cddfSDavid du Colombier gx_cpath_accum_end(const gx_device_cpath_accum * padev, gx_clip_path * pcpath)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier     int code = (*dev_proc(padev, close_device)) ((gx_device *) padev);
1257dd7cddfSDavid du Colombier     /* Make an entire clipping path so we can use cpath_assign. */
1267dd7cddfSDavid du Colombier     gx_clip_path apath;
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier     if (code < 0)
1297dd7cddfSDavid du Colombier 	return code;
1307dd7cddfSDavid du Colombier     gx_cpath_init_local(&apath, padev->list_memory);
1317dd7cddfSDavid du Colombier     apath.rect_list->list = padev->list;
132*593dc095SDavid du Colombier     if (padev->list.count == 0)
133*593dc095SDavid du Colombier 	apath.path.bbox.p.x = apath.path.bbox.p.y =
134*593dc095SDavid du Colombier 	apath.path.bbox.q.x = apath.path.bbox.q.y = 0;
135*593dc095SDavid du Colombier     else {
1367dd7cddfSDavid du Colombier 	apath.path.bbox.p.x = int2fixed(padev->bbox.p.x);
1377dd7cddfSDavid du Colombier 	apath.path.bbox.p.y = int2fixed(padev->bbox.p.y);
1387dd7cddfSDavid du Colombier 	apath.path.bbox.q.x = int2fixed(padev->bbox.q.x);
1397dd7cddfSDavid du Colombier 	apath.path.bbox.q.y = int2fixed(padev->bbox.q.y);
140*593dc095SDavid du Colombier     }
141*593dc095SDavid du Colombier     /* indicate that the bbox is accurate */
142*593dc095SDavid du Colombier     apath.path.bbox_accurate = 1;
1437dd7cddfSDavid du Colombier     /* Note that the result of the intersection might be */
1447dd7cddfSDavid du Colombier     /* a single rectangle.  This will cause clip_path_is_rect.. */
1457dd7cddfSDavid du Colombier     /* to return true.  This, in turn, requires that */
1467dd7cddfSDavid du Colombier     /* we set apath.inner_box correctly. */
1477dd7cddfSDavid du Colombier     if (clip_list_is_rectangle(&padev->list))
1487dd7cddfSDavid du Colombier 	apath.inner_box = apath.path.bbox;
1497dd7cddfSDavid du Colombier     else {
1507dd7cddfSDavid du Colombier 	/* The quick check must fail. */
1517dd7cddfSDavid du Colombier 	apath.inner_box.p.x = apath.inner_box.p.y = 0;
1527dd7cddfSDavid du Colombier 	apath.inner_box.q.x = apath.inner_box.q.y = 0;
1537dd7cddfSDavid du Colombier     }
1547dd7cddfSDavid du Colombier     gx_cpath_set_outer_box(&apath);
1557dd7cddfSDavid du Colombier     apath.path_valid = false;
156*593dc095SDavid du Colombier     apath.id = gs_next_ids(padev->list_memory, 1);	/* path changed => change id */
1577dd7cddfSDavid du Colombier     gx_cpath_assign_free(pcpath, &apath);
1587dd7cddfSDavid du Colombier     return 0;
1597dd7cddfSDavid du Colombier }
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier /* Discard an accumulator in case of error. */
1627dd7cddfSDavid du Colombier void
gx_cpath_accum_discard(gx_device_cpath_accum * padev)1637dd7cddfSDavid du Colombier gx_cpath_accum_discard(gx_device_cpath_accum * padev)
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier     gx_clip_list_free(&padev->list, padev->list_memory);
1667dd7cddfSDavid du Colombier }
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier /* Intersect two clipping paths using an accumulator. */
1697dd7cddfSDavid du Colombier int
gx_cpath_intersect_path_slow(gx_clip_path * pcpath,gx_path * ppath,int rule,gs_imager_state * pis)1707dd7cddfSDavid du Colombier gx_cpath_intersect_path_slow(gx_clip_path * pcpath, gx_path * ppath,
1717dd7cddfSDavid du Colombier 			     int rule, gs_imager_state *pis)
1727dd7cddfSDavid du Colombier {
1737dd7cddfSDavid du Colombier     gs_logical_operation_t save_lop = gs_current_logical_op_inline(pis);
1747dd7cddfSDavid du Colombier     gx_device_cpath_accum adev;
1757dd7cddfSDavid du Colombier     gx_device_color devc;
1767dd7cddfSDavid du Colombier     gx_fill_params params;
1777dd7cddfSDavid du Colombier     int code;
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier     gx_cpath_accum_begin(&adev, pcpath->path.memory);
180*593dc095SDavid du Colombier     set_nonclient_dev_color(&devc, 0);	/* arbitrary, but not transparent */
1817dd7cddfSDavid du Colombier     gs_set_logical_op_inline(pis, lop_default);
1827dd7cddfSDavid du Colombier     params.rule = rule;
1837dd7cddfSDavid du Colombier     params.adjust.x = params.adjust.y = fixed_half;
1847dd7cddfSDavid du Colombier     params.flatness = gs_currentflat_inline(pis);
1857dd7cddfSDavid du Colombier     params.fill_zero_width = true;
1867dd7cddfSDavid du Colombier     code = gx_fill_path_only(ppath, (gx_device *)&adev, pis,
1877dd7cddfSDavid du Colombier 			     &params, &devc, pcpath);
1887dd7cddfSDavid du Colombier     if (code < 0 || (code = gx_cpath_accum_end(&adev, pcpath)) < 0)
1897dd7cddfSDavid du Colombier 	gx_cpath_accum_discard(&adev);
1907dd7cddfSDavid du Colombier     gs_set_logical_op_inline(pis, save_lop);
1917dd7cddfSDavid du Colombier     return code;
1927dd7cddfSDavid du Colombier }
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier /* ------ Device implementation ------ */
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier #ifdef DEBUG
1977dd7cddfSDavid du Colombier /* Validate a clipping path after accumulation. */
1987dd7cddfSDavid du Colombier private bool
clip_list_validate(const gx_clip_list * clp)1997dd7cddfSDavid du Colombier clip_list_validate(const gx_clip_list * clp)
2007dd7cddfSDavid du Colombier {
2017dd7cddfSDavid du Colombier     if (clp->count <= 1)
2027dd7cddfSDavid du Colombier 	return (clp->head == 0 && clp->tail == 0 &&
2037dd7cddfSDavid du Colombier 		clp->single.next == 0 && clp->single.prev == 0);
2047dd7cddfSDavid du Colombier     else {
2057dd7cddfSDavid du Colombier 	const gx_clip_rect *prev = clp->head;
2067dd7cddfSDavid du Colombier 	const gx_clip_rect *ptr;
2077dd7cddfSDavid du Colombier 	bool ok = true;
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier 	while ((ptr = prev->next) != 0) {
2107dd7cddfSDavid du Colombier 	    if (ptr->ymin > ptr->ymax || ptr->xmin > ptr->xmax ||
2117dd7cddfSDavid du Colombier 		!(ptr->ymin >= prev->ymax ||
2127dd7cddfSDavid du Colombier 		  (ptr->ymin == prev->ymin &&
2137dd7cddfSDavid du Colombier 		   ptr->ymax == prev->ymax &&
2147dd7cddfSDavid du Colombier 		   ptr->xmin >= prev->xmax)) ||
2157dd7cddfSDavid du Colombier 		ptr->prev != prev
2167dd7cddfSDavid du Colombier 		) {
2177dd7cddfSDavid du Colombier 		clip_rect_print('q', "WRONG:", ptr);
2187dd7cddfSDavid du Colombier 		ok = false;
2197dd7cddfSDavid du Colombier 	    }
2207dd7cddfSDavid du Colombier 	    prev = ptr;
2217dd7cddfSDavid du Colombier 	}
2227dd7cddfSDavid du Colombier 	return ok && prev == clp->tail;
2237dd7cddfSDavid du Colombier     }
2247dd7cddfSDavid du Colombier }
2257dd7cddfSDavid du Colombier #endif /* DEBUG */
2267dd7cddfSDavid du Colombier 
2277dd7cddfSDavid du Colombier /* Initialize the accumulation device. */
2287dd7cddfSDavid du Colombier private int
accum_open(register gx_device * dev)2297dd7cddfSDavid du Colombier accum_open(register gx_device * dev)
2307dd7cddfSDavid du Colombier {
2317dd7cddfSDavid du Colombier     gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier     gx_clip_list_init(&adev->list);
2347dd7cddfSDavid du Colombier     adev->bbox.p.x = adev->bbox.p.y = max_int;
2357dd7cddfSDavid du Colombier     adev->bbox.q.x = adev->bbox.q.y = min_int;
2367dd7cddfSDavid du Colombier     adev->clip_box.p.x = adev->clip_box.p.y = min_int;
2377dd7cddfSDavid du Colombier     adev->clip_box.q.x = adev->clip_box.q.y = max_int;
2387dd7cddfSDavid du Colombier     return 0;
2397dd7cddfSDavid du Colombier }
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier /* Close the accumulation device. */
2427dd7cddfSDavid du Colombier private int
accum_close(gx_device * dev)2437dd7cddfSDavid du Colombier accum_close(gx_device * dev)
2447dd7cddfSDavid du Colombier {
2457dd7cddfSDavid du Colombier     gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier     adev->list.xmin = adev->bbox.p.x;
2487dd7cddfSDavid du Colombier     adev->list.xmax = adev->bbox.q.x;
2497dd7cddfSDavid du Colombier #ifdef DEBUG
2507dd7cddfSDavid du Colombier     if (gs_debug_c('q')) {
2517dd7cddfSDavid du Colombier 	gx_clip_rect *rp =
2527dd7cddfSDavid du Colombier 	    (adev->list.count <= 1 ? &adev->list.single : adev->list.head);
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	dlprintf6("[q]list at 0x%lx, count=%d, head=0x%lx, tail=0x%lx, xrange=(%d,%d):\n",
2557dd7cddfSDavid du Colombier 		  (ulong) & adev->list, adev->list.count,
2567dd7cddfSDavid du Colombier 		  (ulong) adev->list.head, (ulong) adev->list.tail,
2577dd7cddfSDavid du Colombier 		  adev->list.xmin, adev->list.xmax);
2587dd7cddfSDavid du Colombier 	while (rp != 0) {
2597dd7cddfSDavid du Colombier 	    clip_rect_print('q', "   ", rp);
2607dd7cddfSDavid du Colombier 	    rp = rp->next;
2617dd7cddfSDavid du Colombier 	}
2627dd7cddfSDavid du Colombier     }
2637dd7cddfSDavid du Colombier     if (!clip_list_validate(&adev->list)) {
2647dd7cddfSDavid du Colombier 	lprintf1("[q]Bad clip list 0x%lx!\n", (ulong) & adev->list);
2657dd7cddfSDavid du Colombier 	return_error(gs_error_Fatal);
2667dd7cddfSDavid du Colombier     }
2677dd7cddfSDavid du Colombier #endif
2687dd7cddfSDavid du Colombier     return 0;
2697dd7cddfSDavid du Colombier }
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier /* Accumulate one rectangle. */
2727dd7cddfSDavid du Colombier /* Allocate a rectangle to be added to the list. */
2737dd7cddfSDavid du Colombier static const gx_clip_rect clip_head_rect = {
2747dd7cddfSDavid du Colombier     0, 0, min_int, min_int, min_int, min_int
2757dd7cddfSDavid du Colombier };
2767dd7cddfSDavid du Colombier static const gx_clip_rect clip_tail_rect = {
2777dd7cddfSDavid du Colombier     0, 0, max_int, max_int, max_int, max_int
2787dd7cddfSDavid du Colombier };
2797dd7cddfSDavid du Colombier private gx_clip_rect *
accum_alloc_rect(gx_device_cpath_accum * adev)2807dd7cddfSDavid du Colombier accum_alloc_rect(gx_device_cpath_accum * adev)
2817dd7cddfSDavid du Colombier {
2827dd7cddfSDavid du Colombier     gs_memory_t *mem = adev->list_memory;
2837dd7cddfSDavid du Colombier     gx_clip_rect *ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
2847dd7cddfSDavid du Colombier 				       "accum_alloc_rect");
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier     if (ar == 0)
2877dd7cddfSDavid du Colombier 	return 0;
2887dd7cddfSDavid du Colombier     if (adev->list.count == 2) {
2897dd7cddfSDavid du Colombier 	/* We're switching from a single rectangle to a list. */
2907dd7cddfSDavid du Colombier 	/* Allocate the head and tail entries. */
2917dd7cddfSDavid du Colombier 	gx_clip_rect *head = ar;
2927dd7cddfSDavid du Colombier 	gx_clip_rect *tail =
2937dd7cddfSDavid du Colombier 	    gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
2947dd7cddfSDavid du Colombier 			    "accum_alloc_rect(tail)");
2957dd7cddfSDavid du Colombier 	gx_clip_rect *single =
2967dd7cddfSDavid du Colombier 	    gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
2977dd7cddfSDavid du Colombier 			    "accum_alloc_rect(single)");
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
3007dd7cddfSDavid du Colombier 			     "accum_alloc_rect(head)");
3017dd7cddfSDavid du Colombier 	if (tail == 0 || single == 0 || ar == 0) {
3027dd7cddfSDavid du Colombier 	    gs_free_object(mem, ar, "accum_alloc_rect");
3037dd7cddfSDavid du Colombier 	    gs_free_object(mem, single, "accum_alloc_rect(single)");
3047dd7cddfSDavid du Colombier 	    gs_free_object(mem, tail, "accum_alloc_rect(tail)");
3057dd7cddfSDavid du Colombier 	    gs_free_object(mem, head, "accum_alloc_rect(head)");
3067dd7cddfSDavid du Colombier 	    return 0;
3077dd7cddfSDavid du Colombier 	}
3087dd7cddfSDavid du Colombier 	*head = clip_head_rect;
3097dd7cddfSDavid du Colombier 	head->next = single;
3107dd7cddfSDavid du Colombier 	*single = adev->list.single;
3117dd7cddfSDavid du Colombier 	single->prev = head;
3127dd7cddfSDavid du Colombier 	single->next = tail;
3137dd7cddfSDavid du Colombier 	*tail = clip_tail_rect;
3147dd7cddfSDavid du Colombier 	tail->prev = single;
3157dd7cddfSDavid du Colombier 	adev->list.head = head;
3167dd7cddfSDavid du Colombier 	adev->list.tail = tail;
3177dd7cddfSDavid du Colombier     }
3187dd7cddfSDavid du Colombier     return ar;
3197dd7cddfSDavid du Colombier }
3207dd7cddfSDavid du Colombier #define ACCUM_ALLOC(s, ar, px, py, qx, qy)\
3217dd7cddfSDavid du Colombier 	if (++(adev->list.count) == 1)\
3227dd7cddfSDavid du Colombier 	  ar = &adev->list.single;\
3237dd7cddfSDavid du Colombier 	else if ((ar = accum_alloc_rect(adev)) == 0)\
3247dd7cddfSDavid du Colombier 	  return_error(gs_error_VMerror);\
3257dd7cddfSDavid du Colombier 	ACCUM_SET(s, ar, px, py, qx, qy)
3267dd7cddfSDavid du Colombier #define ACCUM_SET(s, ar, px, py, qx, qy)\
3277dd7cddfSDavid du Colombier 	(ar)->xmin = px, (ar)->ymin = py, (ar)->xmax = qx, (ar)->ymax = qy;\
3287dd7cddfSDavid du Colombier 	clip_rect_print('Q', s, ar)
3297dd7cddfSDavid du Colombier /* Link or unlink a rectangle in the list. */
3307dd7cddfSDavid du Colombier #define ACCUM_ADD_LAST(ar)\
3317dd7cddfSDavid du Colombier 	ACCUM_ADD_BEFORE(ar, adev->list.tail)
3327dd7cddfSDavid du Colombier #define ACCUM_ADD_AFTER(ar, rprev)\
3337dd7cddfSDavid du Colombier 	ar->prev = (rprev), (ar->next = (rprev)->next)->prev = ar,\
3347dd7cddfSDavid du Colombier 	  (rprev)->next = ar
3357dd7cddfSDavid du Colombier #define ACCUM_ADD_BEFORE(ar, rnext)\
3367dd7cddfSDavid du Colombier 	(ar->prev = (rnext)->prev)->next = ar, ar->next = (rnext),\
3377dd7cddfSDavid du Colombier 	  (rnext)->prev = ar
3387dd7cddfSDavid du Colombier #define ACCUM_REMOVE(ar)\
3397dd7cddfSDavid du Colombier 	ar->next->prev = ar->prev, ar->prev->next = ar->next
3407dd7cddfSDavid du Colombier /* Free a rectangle that was removed from the list. */
3417dd7cddfSDavid du Colombier #define ACCUM_FREE(s, ar)\
3427dd7cddfSDavid du Colombier 	if (--(adev->list.count)) {\
3437dd7cddfSDavid du Colombier 	  clip_rect_print('Q', s, ar);\
3447dd7cddfSDavid du Colombier 	  gs_free_object(adev->list_memory, ar, "accum_rect");\
3457dd7cddfSDavid du Colombier 	}
3467dd7cddfSDavid du Colombier /*
3477dd7cddfSDavid du Colombier  * Add a rectangle to the list.  It would be wonderful if rectangles
3487dd7cddfSDavid du Colombier  * were always disjoint and always presented in the correct order,
3497dd7cddfSDavid du Colombier  * but they aren't: the fill loop works by trapezoids, not by scan lines,
3507dd7cddfSDavid du Colombier  * and may produce slightly overlapping rectangles because of "fattening".
3517dd7cddfSDavid du Colombier  * All we can count on is that they are approximately disjoint and
3527dd7cddfSDavid du Colombier  * approximately in order.
3537dd7cddfSDavid du Colombier  *
3547dd7cddfSDavid du Colombier  * Because of the way the fill loop handles a path that is just a single
3557dd7cddfSDavid du Colombier  * rectangle, we take special care to merge Y-adjacent rectangles when
3567dd7cddfSDavid du Colombier  * this is possible.
3577dd7cddfSDavid du Colombier  */
3587dd7cddfSDavid du Colombier private int
accum_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)3597dd7cddfSDavid du Colombier accum_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
3607dd7cddfSDavid du Colombier 		     gx_color_index color)
3617dd7cddfSDavid du Colombier {
3627dd7cddfSDavid du Colombier     gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
3637dd7cddfSDavid du Colombier     int xe = x + w, ye = y + h;
3647dd7cddfSDavid du Colombier     gx_clip_rect *nr;
3657dd7cddfSDavid du Colombier     gx_clip_rect *ar;
3667dd7cddfSDavid du Colombier     register gx_clip_rect *rptr;
3677dd7cddfSDavid du Colombier     int ymin, ymax;
3687dd7cddfSDavid du Colombier 
3697dd7cddfSDavid du Colombier     /* Clip the rectangle being added. */
3707dd7cddfSDavid du Colombier     if (y < adev->clip_box.p.y)
3717dd7cddfSDavid du Colombier 	y = adev->clip_box.p.y;
3727dd7cddfSDavid du Colombier     if (ye > adev->clip_box.q.y)
3737dd7cddfSDavid du Colombier 	ye = adev->clip_box.q.y;
3747dd7cddfSDavid du Colombier     if (y >= ye)
3757dd7cddfSDavid du Colombier 	return 0;
3767dd7cddfSDavid du Colombier     if (x < adev->clip_box.p.x)
3777dd7cddfSDavid du Colombier 	x = adev->clip_box.p.x;
3787dd7cddfSDavid du Colombier     if (xe > adev->clip_box.q.x)
3797dd7cddfSDavid du Colombier 	xe = adev->clip_box.q.x;
3807dd7cddfSDavid du Colombier     if (x >= xe)
3817dd7cddfSDavid du Colombier 	return 0;
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier     /* Update the bounding box. */
3847dd7cddfSDavid du Colombier     if (x < adev->bbox.p.x)
3857dd7cddfSDavid du Colombier 	adev->bbox.p.x = x;
3867dd7cddfSDavid du Colombier     if (y < adev->bbox.p.y)
3877dd7cddfSDavid du Colombier 	adev->bbox.p.y = y;
3887dd7cddfSDavid du Colombier     if (xe > adev->bbox.q.x)
3897dd7cddfSDavid du Colombier 	adev->bbox.q.x = xe;
3907dd7cddfSDavid du Colombier     if (ye > adev->bbox.q.y)
3917dd7cddfSDavid du Colombier 	adev->bbox.q.y = ye;
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier top:
3947dd7cddfSDavid du Colombier     if (adev->list.count == 0) {	/* very first rectangle */
3957dd7cddfSDavid du Colombier 	adev->list.count = 1;
3967dd7cddfSDavid du Colombier 	ACCUM_SET("single", &adev->list.single, x, y, xe, ye);
3977dd7cddfSDavid du Colombier 	return 0;
3987dd7cddfSDavid du Colombier     }
3997dd7cddfSDavid du Colombier     if (adev->list.count == 1) {	/* check for Y merging */
4007dd7cddfSDavid du Colombier 	rptr = &adev->list.single;
4017dd7cddfSDavid du Colombier 	if (x == rptr->xmin && xe == rptr->xmax &&
4027dd7cddfSDavid du Colombier 	    y <= rptr->ymax && ye >= rptr->ymin
4037dd7cddfSDavid du Colombier 	    ) {
4047dd7cddfSDavid du Colombier 	    if (y < rptr->ymin)
4057dd7cddfSDavid du Colombier 		rptr->ymin = y;
4067dd7cddfSDavid du Colombier 	    if (ye > rptr->ymax)
4077dd7cddfSDavid du Colombier 		rptr->ymax = ye;
4087dd7cddfSDavid du Colombier 	    return 0;
4097dd7cddfSDavid du Colombier 	}
4107dd7cddfSDavid du Colombier     }
4117dd7cddfSDavid du Colombier     else
4127dd7cddfSDavid du Colombier 	rptr = adev->list.tail->prev;
4137dd7cddfSDavid du Colombier     if (y >= rptr->ymax) {
4147dd7cddfSDavid du Colombier 	if (y == rptr->ymax && x == rptr->xmin && xe == rptr->xmax &&
4157dd7cddfSDavid du Colombier 	    (rptr->prev == 0 || y != rptr->prev->ymax)
4167dd7cddfSDavid du Colombier 	    ) {
4177dd7cddfSDavid du Colombier 	    rptr->ymax = ye;
4187dd7cddfSDavid du Colombier 	    return 0;
4197dd7cddfSDavid du Colombier 	}
4207dd7cddfSDavid du Colombier 	ACCUM_ALLOC("app.y", nr, x, y, xe, ye);
4217dd7cddfSDavid du Colombier 	ACCUM_ADD_LAST(nr);
4227dd7cddfSDavid du Colombier 	return 0;
4237dd7cddfSDavid du Colombier     } else if (y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmin) {
4247dd7cddfSDavid du Colombier 	if (x <= rptr->xmax) {
4257dd7cddfSDavid du Colombier 	    if (xe > rptr->xmax)
4267dd7cddfSDavid du Colombier 		rptr->xmax = xe;
4277dd7cddfSDavid du Colombier 	    return 0;
4287dd7cddfSDavid du Colombier 	}
4297dd7cddfSDavid du Colombier 	ACCUM_ALLOC("app.x", nr, x, y, xe, ye);
4307dd7cddfSDavid du Colombier 	ACCUM_ADD_LAST(nr);
4317dd7cddfSDavid du Colombier 	return 0;
4327dd7cddfSDavid du Colombier     }
4337dd7cddfSDavid du Colombier     ACCUM_ALLOC("accum", nr, x, y, xe, ye);
4347dd7cddfSDavid du Colombier     rptr = adev->list.tail->prev;
4357dd7cddfSDavid du Colombier     /* Work backwards till we find the insertion point. */
4367dd7cddfSDavid du Colombier     while (ye <= rptr->ymin)
4377dd7cddfSDavid du Colombier 	rptr = rptr->prev;
4387dd7cddfSDavid du Colombier     ymin = rptr->ymin;
4397dd7cddfSDavid du Colombier     ymax = rptr->ymax;
4407dd7cddfSDavid du Colombier     if (ye > ymax) {
4417dd7cddfSDavid du Colombier 	if (y >= ymax) {	/* Insert between two bands. */
4427dd7cddfSDavid du Colombier 	    ACCUM_ADD_AFTER(nr, rptr);
4437dd7cddfSDavid du Colombier 	    return 0;
4447dd7cddfSDavid du Colombier 	}
4457dd7cddfSDavid du Colombier 	/* Split off the top part of the new rectangle. */
4467dd7cddfSDavid du Colombier 	ACCUM_ALLOC("a.top", ar, x, ymax, xe, ye);
4477dd7cddfSDavid du Colombier 	ACCUM_ADD_AFTER(ar, rptr);
4487dd7cddfSDavid du Colombier 	ye = nr->ymax = ymax;
4497dd7cddfSDavid du Colombier 	clip_rect_print('Q', " ymax", nr);
4507dd7cddfSDavid du Colombier     }
4517dd7cddfSDavid du Colombier     /* Here we know ymin < ye <= ymax; */
4527dd7cddfSDavid du Colombier     /* rptr points to the last node with this value of ymin/ymax. */
4537dd7cddfSDavid du Colombier     /* If necessary, split off the part of the existing band */
4547dd7cddfSDavid du Colombier     /* that is above the new band. */
4557dd7cddfSDavid du Colombier     if (ye < ymax) {
4567dd7cddfSDavid du Colombier 	gx_clip_rect *rsplit = rptr;
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier 	while (rsplit->ymax == ymax) {
4597dd7cddfSDavid du Colombier 	    ACCUM_ALLOC("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
4607dd7cddfSDavid du Colombier 	    ACCUM_ADD_AFTER(ar, rptr);
4617dd7cddfSDavid du Colombier 	    rsplit->ymax = ye;
4627dd7cddfSDavid du Colombier 	    rsplit = rsplit->prev;
4637dd7cddfSDavid du Colombier 	}
4647dd7cddfSDavid du Colombier 	ymax = ye;
4657dd7cddfSDavid du Colombier     }
4667dd7cddfSDavid du Colombier     /* Now ye = ymax.  If necessary, split off the part of the */
4677dd7cddfSDavid du Colombier     /* existing band that is below the new band. */
4687dd7cddfSDavid du Colombier     if (y > ymin) {
4697dd7cddfSDavid du Colombier 	gx_clip_rect *rbot = rptr, *rsplit;
4707dd7cddfSDavid du Colombier 
4717dd7cddfSDavid du Colombier 	while (rbot->prev->ymin == ymin)
4727dd7cddfSDavid du Colombier 	    rbot = rbot->prev;
4737dd7cddfSDavid du Colombier 	for (rsplit = rbot;;) {
4747dd7cddfSDavid du Colombier 	    ACCUM_ALLOC("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
4757dd7cddfSDavid du Colombier 	    ACCUM_ADD_BEFORE(ar, rbot);
4767dd7cddfSDavid du Colombier 	    rsplit->ymin = y;
4777dd7cddfSDavid du Colombier 	    if (rsplit == rptr)
4787dd7cddfSDavid du Colombier 		break;
4797dd7cddfSDavid du Colombier 	    rsplit = rsplit->next;
4807dd7cddfSDavid du Colombier 	}
4817dd7cddfSDavid du Colombier 	ymin = y;
4827dd7cddfSDavid du Colombier     }
4837dd7cddfSDavid du Colombier     /* Now y <= ymin as well.  (y < ymin is possible.) */
4847dd7cddfSDavid du Colombier     nr->ymin = ymin;
4857dd7cddfSDavid du Colombier     /* Search for the X insertion point. */
4867dd7cddfSDavid du Colombier     for (; rptr->ymin == ymin; rptr = rptr->prev) {
4877dd7cddfSDavid du Colombier 	if (xe < rptr->xmin)
4887dd7cddfSDavid du Colombier 	    continue;		/* still too far to right */
4897dd7cddfSDavid du Colombier 	if (x > rptr->xmax)
4907dd7cddfSDavid du Colombier 	    break;		/* disjoint */
4917dd7cddfSDavid du Colombier 	/* The new rectangle overlaps an existing one.  Merge them. */
4927dd7cddfSDavid du Colombier 	if (xe > rptr->xmax) {
4937dd7cddfSDavid du Colombier 	    rptr->xmax = nr->xmax;	/* might be > xe if */
4947dd7cddfSDavid du Colombier 	    /* we already did a merge */
4957dd7cddfSDavid du Colombier 	    clip_rect_print('Q', "widen", rptr);
4967dd7cddfSDavid du Colombier 	}
4977dd7cddfSDavid du Colombier 	ACCUM_FREE("free", nr);
4987dd7cddfSDavid du Colombier 	if (x >= rptr->xmin)
4997dd7cddfSDavid du Colombier 	    goto out;
5007dd7cddfSDavid du Colombier 	/* Might overlap other rectangles to the left. */
5017dd7cddfSDavid du Colombier 	rptr->xmin = x;
5027dd7cddfSDavid du Colombier 	nr = rptr;
5037dd7cddfSDavid du Colombier 	ACCUM_REMOVE(rptr);
5047dd7cddfSDavid du Colombier 	clip_rect_print('Q', "merge", nr);
5057dd7cddfSDavid du Colombier     }
5067dd7cddfSDavid du Colombier     ACCUM_ADD_AFTER(nr, rptr);
5077dd7cddfSDavid du Colombier out:
5087dd7cddfSDavid du Colombier     /* Check whether there are only 0 or 1 rectangles left. */
5097dd7cddfSDavid du Colombier     if (adev->list.count <= 1) {
5107dd7cddfSDavid du Colombier 	/* We're switching from a list to at most 1 rectangle. */
5117dd7cddfSDavid du Colombier 	/* Free the head and tail entries. */
5127dd7cddfSDavid du Colombier 	gs_memory_t *mem = adev->list_memory;
5137dd7cddfSDavid du Colombier 	gx_clip_rect *single = adev->list.head->next;
5147dd7cddfSDavid du Colombier 
5157dd7cddfSDavid du Colombier 	if (single != adev->list.tail) {
5167dd7cddfSDavid du Colombier 	    adev->list.single = *single;
5177dd7cddfSDavid du Colombier 	    gs_free_object(mem, single, "accum_free_rect(single)");
5187dd7cddfSDavid du Colombier 	    adev->list.single.next = adev->list.single.prev = 0;
5197dd7cddfSDavid du Colombier 	}
5207dd7cddfSDavid du Colombier 	gs_free_object(mem, adev->list.tail, "accum_free_rect(tail)");
5217dd7cddfSDavid du Colombier 	gs_free_object(mem, adev->list.head, "accum_free_rect(head)");
5227dd7cddfSDavid du Colombier 	adev->list.head = 0;
5237dd7cddfSDavid du Colombier 	adev->list.tail = 0;
5247dd7cddfSDavid du Colombier     }
5257dd7cddfSDavid du Colombier     /* Check whether there is still more of the new band to process. */
5267dd7cddfSDavid du Colombier     if (y < ymin) {
5277dd7cddfSDavid du Colombier 	/* Continue with the bottom part of the new rectangle. */
5287dd7cddfSDavid du Colombier 	clip_rect_print('Q', " ymin", nr);
5297dd7cddfSDavid du Colombier 	ye = ymin;
5307dd7cddfSDavid du Colombier 	goto top;
5317dd7cddfSDavid du Colombier     }
5327dd7cddfSDavid du Colombier     return 0;
5337dd7cddfSDavid du Colombier }
534