Add pepper_region to pepper 63/96363/2
authorSangjin Lee <lsj119@samsung.com>
Tue, 8 Nov 2016 12:29:31 +0000 (21:29 +0900)
committerSangjin Lee <lsj119@samsung.com>
Thu, 17 Nov 2016 05:52:53 +0000 (14:52 +0900)
For remove dependency of pixman in pepper.
add the pepper_region funtions to the pepper.
this funcions came from the pixman logic. therefor logic and operation
is same form pixman. the difference is just prefix.

Change-Id: I4124791a394657de97073a713abd697bbad8de0c

src/lib/pepper/Makefile.am
src/lib/pepper/pepper-utils.h
src/lib/pepper/utils-region.c [new file with mode: 0644]

index 94a8df4..e100e59 100644 (file)
@@ -27,6 +27,7 @@ libpepper_la_SOURCES = pepper.h                 \
                        utils-map.c              \
                        utils-log.c              \
                        utils-vt.c               \
+                       utils-region.c           \
                        subcompositor.c          \
                        subsurface.c             \
                        misc.c
index d08ebad..9fa9619 100644 (file)
@@ -869,6 +869,130 @@ pepper_virtual_terminal_setup(int tty);
 PEPPER_API void
 pepper_virtual_terminal_restore(void);
 
+/*
+ * Regions
+ */
+typedef enum
+{
+    PEPPER_REGION_OUT,
+    PEPPER_REGION_IN,
+    PEPPER_REGION_PART
+} pepper_region_overlap_t;
+
+typedef struct pepper_region_data      pepper_region_data_t;
+typedef struct pepper_box              pepper_box_t;
+typedef struct pepper_rectangle        pepper_rectangle_t;
+typedef struct pepper_region           pepper_region_t;
+
+struct pepper_region_data {
+    long               size;
+    long               numRects;
+/*  pepper_box_t       rects[size];   in memory but not explicitly declared */
+};
+
+struct pepper_rectangle
+{
+    int32_t x, y;
+    uint32_t width, height;
+};
+
+struct pepper_box
+{
+    int32_t x1, y1, x2, y2;
+};
+
+struct pepper_region
+{
+    pepper_box_t          extents;
+    pepper_region_data_t  *data;
+};
+
+/* creation/destruction */
+PEPPER_API void
+pepper_region_init(pepper_region_t *region);
+PEPPER_API void
+pepper_region_init_rect(pepper_region_t *region,
+                                               int                x,
+                                               int                y,
+                                               unsigned int       width,
+                                               unsigned int       height);
+PEPPER_API pepper_bool_t
+pepper_region_init_rects(pepper_region_t *region,
+                                                const pepper_box_t *boxes,
+                                                int                count);
+PEPPER_API void
+pepper_region_init_with_extents(pepper_region_t *region,
+                                                               pepper_box_t    *extents);
+PEPPER_API void
+pepper_region_fini(pepper_region_t *region);
+
+
+/* manipulation */
+PEPPER_API void
+pepper_region_translate(pepper_region_t *region,
+                                               int                x,
+                                               int                y);
+PEPPER_API pepper_bool_t
+pepper_region_copy(pepper_region_t *dest, pepper_region_t *source);
+PEPPER_API pepper_bool_t
+pepper_region_intersect(pepper_region_t *new_reg,
+                                               pepper_region_t *reg1,
+                                               pepper_region_t *reg2);
+PEPPER_API pepper_bool_t
+pepper_region_union(pepper_region_t *new_reg,
+                                       pepper_region_t *reg1,
+                                       pepper_region_t *reg2);
+PEPPER_API pepper_bool_t
+pepper_region_intersect_rect(pepper_region_t *dest,
+                                                        pepper_region_t *source,
+                                                        int                x,
+                                                        int                y,
+                                                        unsigned int       width,
+                                                        unsigned int       height);
+PEPPER_API pepper_bool_t
+pepper_region_union_rect(pepper_region_t *dest,
+                                                pepper_region_t *source,
+                                                int                x,
+                                                int                y,
+                                                unsigned int       width,
+                                                unsigned int       height);
+PEPPER_API pepper_bool_t
+pepper_region_subtract(pepper_region_t *reg_d,
+                                          pepper_region_t *reg_m,
+                                          pepper_region_t *reg_s);
+PEPPER_API pepper_bool_t
+pepper_region_inverse(pepper_region_t *new_reg,
+                                         pepper_region_t *reg1,
+                                         pepper_box_t    *inv_rect);
+PEPPER_API pepper_bool_t
+pepper_region_contains_point(pepper_region_t *region,
+                                                        int                x,
+                                                        int                y,
+                                                        pepper_box_t    *box);
+PEPPER_API pepper_region_overlap_t
+pepper_region_contains_rectangle(pepper_region_t *region,
+                                                                pepper_box_t    *prect);
+PEPPER_API pepper_bool_t
+pepper_region_not_empty(pepper_region_t *region);
+PEPPER_API pepper_box_t *
+pepper_region_extents(pepper_region_t *region);
+PEPPER_API int
+pepper_region_n_rects(pepper_region_t *region);
+PEPPER_API pepper_box_t *
+pepper_region_rectangles(pepper_region_t *region,
+                                                int               *n_rects);
+PEPPER_API pepper_bool_t
+pepper_region_equal(pepper_region_t *region1,
+                                       pepper_region_t *region2);
+PEPPER_API pepper_bool_t
+pepper_region_selfcheck(pepper_region_t *region);
+PEPPER_API void
+pepper_region_reset(pepper_region_t *region,
+                                       pepper_box_t    *box);
+PEPPER_API void
+pepper_region_clear(pepper_region_t *region);
+PEPPER_API int
+pepper_region_print(pepper_region_t *region);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/lib/pepper/utils-region.c b/src/lib/pepper/utils-region.c
new file mode 100644 (file)
index 0000000..1196726
--- /dev/null
@@ -0,0 +1,2600 @@
+/*
+ * Copyright 1987, 1988, 1989, 1998  The Open Group
+ * 
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * 
+ * Except as contained in this notice, the name of The Open Group shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from The Open Group.
+ * 
+ * Copyright 1987, 1988, 1989 by
+ * Digital Equipment Corporation, Maynard, Massachusetts.
+ * 
+ *                    All Rights Reserved
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ * 
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Copyright © 1998 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Keith Packard not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission.  Keith Packard makes no
+ * representations about the suitability of this software for any purpose.  It
+ * is provided "as is" without express or implied warranty.
+ *
+ * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include "pepper-utils.h"\r
+\r
+typedef int64_t                 overflow_int_t;\r
+\r
+#ifndef INT32_MIN
+# define INT32_MIN              (-2147483647-1)
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX              (2147483647)
+#endif
+\r
+#if defined (__GNUC__)\r
+#  define unlikely(expr) __builtin_expect ((expr), 0)
+#else
+#  define unlikely(expr)  (expr)
+#endif
+\r
+#define return_if_fail(expr)                                            \
+    do                                                                  \
+    {                                                                   \
+       if (unlikely (!(expr)))                                         \
+       {                                                               \
+           PEPPER_ERROR("%s\n", "The expression " # expr " was false"); \\r
+           return;                                                     \
+       }                                                               \
+    }                                                                   \
+    while (0)
+
+#define return_val_if_fail(expr, retval)                                \
+    do                                                                  \
+    {                                                                   \
+       if (unlikely (!(expr)))                                         \
+       {                                                               \
+           PEPPER_ERROR("%s\n", "The expression " # expr " was false"); \\r
+           return (retval);                                            \
+       }                                                               \
+    }                                                                   \
+    while (0)
+
+#define critical_if_fail(expr)                                         \
+    do                                                                 \
+    {                                                                  \
+       if (unlikely (!(expr)))                                         \
+           PEPPER_ERROR("%s\n", "The expression " # expr " was false"); \\r
+    }                                                                  \
+    while (0)
+\r
+#define PIXREGION_NIL(reg) ((reg)->data && !(reg)->data->numRects)\r
+/* not a region */
+#define PIXREGION_NAR(reg)      ((reg)->data == pepper_broken_data)\r
+#define PIXREGION_NUMRECTS(reg) ((reg)->data ? (reg)->data->numRects : 1)
+#define PIXREGION_SIZE(reg) ((reg)->data ? (reg)->data->size : 0)
+#define PIXREGION_RECTS(reg) \
+    ((reg)->data ? (pepper_box_t *)((reg)->data + 1) \\r
+     : &(reg)->extents)
+#define PIXREGION_BOXPTR(reg) ((pepper_box_t *)((reg)->data + 1))\r
+#define PIXREGION_BOX(reg, i) (&PIXREGION_BOXPTR (reg)[i])
+#define PIXREGION_TOP(reg) PIXREGION_BOX (reg, (reg)->data->numRects)
+#define PIXREGION_END(reg) PIXREGION_BOX (reg, (reg)->data->numRects - 1)
+
+#define GOOD_RECT(rect) ((rect)->x1 < (rect)->x2 && (rect)->y1 < (rect)->y2)
+#define BAD_RECT(rect) ((rect)->x1 > (rect)->x2 || (rect)->y1 > (rect)->y2)
+
+#ifdef DEBUG
+
+#define GOOD(reg)                                                      \
+    do                                                                 \
+    {                                                                  \
+       if (!pepper_region_selfcheck (reg))                                     \\r
+           PEPPER_ERROR("%s","Malformed region " # reg);       \\r
+    } while (0)
+
+#else
+
+#define GOOD(reg)
+
+#endif
+
+static const pepper_box_t pepper_region_empty_box_ = { 0, 0, 0, 0 };\r
+static const pepper_region_data_t pepper_region_empty_data_ = { 0, 0 };\r
+#if defined (__llvm__) && !defined (__clang__)
+static const volatile pepper_region_data_t pepper_region_broken_data_ = { 0, 0 };\r
+#else
+static const pepper_region_data_t pepper_region_broken_data_ = { 0, 0 };\r
+#endif
+
+static pepper_box_t *pepper_region_empty_box =\r
+    (pepper_box_t *)&pepper_region_empty_box_;\r
+static pepper_region_data_t *pepper_region_empty_data =\r
+    (pepper_region_data_t *)&pepper_region_empty_data_;\r
+static pepper_region_data_t *pepper_broken_data =\r
+    (pepper_region_data_t *)&pepper_region_broken_data_;\r
+
+static pepper_bool_t\r
+pepper_break (pepper_region_t *region);\r
+
+/*
+ * The functions in this file implement the Region abstraction used extensively
+ * throughout the X11 sample server. A Region is simply a set of disjoint
+ * (non-overlapping) rectangles, plus an "extent" rectangle which is the
+ * smallest single rectangle that contains all the non-overlapping rectangles.
+ *
+ * A Region is implemented as a "y-x-banded" array of rectangles.  This array
+ * imposes two degrees of order.  First, all rectangles are sorted by top side
+ * y coordinate first (y1), and then by left side x coordinate (x1).
+ *
+ * Furthermore, the rectangles are grouped into "bands".  Each rectangle in a
+ * band has the same top y coordinate (y1), and each has the same bottom y
+ * coordinate (y2).  Thus all rectangles in a band differ only in their left
+ * and right side (x1 and x2).  Bands are implicit in the array of rectangles:
+ * there is no separate list of band start pointers.
+ *
+ * The y-x band representation does not minimize rectangles.  In particular,
+ * if a rectangle vertically crosses a band (the rectangle has scanlines in
+ * the y1 to y2 area spanned by the band), then the rectangle may be broken
+ * down into two or more smaller rectangles stacked one atop the other.
+ *
+ *  -----------                                    -----------
+ *  |         |                                    |         |             band 0
+ *  |         |  --------                  -----------  --------
+ *  |         |  |      |  in y-x banded    |         |  |      |   band 1
+ *  |         |  |      |  form is         |         |  |      |
+ *  -----------  |      |                  -----------  --------
+ *               |      |                               |      |   band 2
+ *               --------                               --------
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible: no two rectangles within a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course).
+ *
+ * Adam de Boor wrote most of the original region code.  Joel McCormack
+ * substantially modified or rewrote most of the core arithmetic routines, and
+ * added pepper_region_validate in order to support several speed improvements\r
+ * to pepper_region_validate_tree.  Bob Scheifler changed the representation\r
+ * to be more compact when empty or a single rectangle, and did a bunch of
+ * gratuitous reformatting. Carl Worth did further gratuitous reformatting
+ * while re-merging the server and client region code into libpixregion.
+ * Soren Sandmann did even more gratuitous reformatting.
+ */
+
+/*  true iff two Boxes overlap */
+#define EXTENTCHECK(r1, r2)       \
+    (!( ((r1)->x2 <= (r2)->x1)  || \
+        ((r1)->x1 >= (r2)->x2)  || \
+        ((r1)->y2 <= (r2)->y1)  || \
+        ((r1)->y1 >= (r2)->y2) ) )
+
+/* true iff (x,y) is in Box */
+#define INBOX(r, x, y) \
+    ( ((r)->x2 >  x) && \
+      ((r)->x1 <= x) && \
+      ((r)->y2 >  y) && \
+      ((r)->y1 <= y) )
+
+/* true iff Box r1 contains Box r2 */
+#define SUBSUMES(r1, r2)       \
+    ( ((r1)->x1 <= (r2)->x1) && \
+      ((r1)->x2 >= (r2)->x2) && \
+      ((r1)->y1 <= (r2)->y1) && \
+      ((r1)->y2 >= (r2)->y2) )
+
+static size_t
+PIXREGION_SZOF (size_t n)
+{
+    size_t size = n * sizeof(pepper_box_t);\r
+    
+    if (n > UINT32_MAX / sizeof(pepper_box_t))\r
+       return 0;
+
+    if (sizeof(pepper_region_data_t) > UINT32_MAX - size)\r
+       return 0;
+
+    return size + sizeof(pepper_region_data_t);\r
+}
+
+static pepper_region_data_t *\r
+alloc_data (size_t n)
+{
+    size_t sz = PIXREGION_SZOF (n);
+
+    if (!sz)
+       return NULL;
+
+    return malloc (sz);
+}
+
+#define FREE_DATA(reg) if ((reg)->data && (reg)->data->size) free ((reg)->data)
+
+#define RECTALLOC_BAIL(region, n, bail)                                        \
+    do                                                                 \
+    {                                                                  \
+       if (!(region)->data ||                                          \
+           (((region)->data->numRects + (n)) > (region)->data->size))  \
+       {                                                               \
+           if (!pepper_rect_alloc (region, n))                         \\r
+               goto bail;                                              \
+       }                                                               \
+    } while (0)
+
+#define RECTALLOC(region, n)                                           \
+    do                                                                 \
+    {                                                                  \
+       if (!(region)->data ||                                          \
+           (((region)->data->numRects + (n)) > (region)->data->size))  \
+       {                                                               \
+           if (!pepper_rect_alloc (region, n)) {                       \\r
+               return PEPPER_FALSE;                                            \\r
+           }                                                           \
+       }                                                               \
+    } while (0)
+
+#define ADDRECT(next_rect, nx1, ny1, nx2, ny2)      \
+    do                                             \
+    {                                              \
+       next_rect->x1 = nx1;                        \
+       next_rect->y1 = ny1;                        \
+       next_rect->x2 = nx2;                        \
+       next_rect->y2 = ny2;                        \
+       next_rect++;                                \
+    }                                              \
+    while (0)
+
+#define NEWRECT(region, next_rect, nx1, ny1, nx2, ny2)                 \
+    do                                                                 \
+    {                                                                  \
+       if (!(region)->data ||                                          \
+           ((region)->data->numRects == (region)->data->size))         \
+       {                                                               \
+           if (!pepper_rect_alloc (region, 1))                         \\r
+               return PEPPER_FALSE;                                            \\r
+           next_rect = PIXREGION_TOP (region);                         \
+       }                                                               \
+       ADDRECT (next_rect, nx1, ny1, nx2, ny2);                        \
+       region->data->numRects++;                                       \
+       critical_if_fail (region->data->numRects <= region->data->size);                \
+    } while (0)
+
+#define DOWNSIZE(reg, numRects)                                                \
+    do                                                                 \
+    {                                                                  \
+       if (((numRects) < ((reg)->data->size >> 1)) &&                  \
+           ((reg)->data->size > 50))                                   \
+       {                                                               \
+           pepper_region_data_t * new_data;                            \\r
+           size_t data_size = PIXREGION_SZOF (numRects);               \
+                                                                       \
+           if (!data_size)                                             \
+           {                                                           \
+               new_data = NULL;                                        \
+           }                                                           \
+           else                                                        \
+           {                                                           \
+               new_data = (pepper_region_data_t *)                     \\r
+                   realloc ((reg)->data, data_size);                   \
+           }                                                           \
+                                                                       \
+           if (new_data)                                               \
+           {                                                           \
+               new_data->size = (numRects);                            \
+               (reg)->data = new_data;                                 \
+           }                                                           \
+       }                                                               \
+    } while (0)
+
+PEPPER_API pepper_bool_t\r
+pepper_region_equal (pepper_region_t *reg1, pepper_region_t *reg2)\r
+{
+    int i;
+    pepper_box_t *rects1;\r
+    pepper_box_t *rects2;\r
+
+    if (reg1->extents.x1 != reg2->extents.x1)
+       return PEPPER_FALSE;\r
+    
+    if (reg1->extents.x2 != reg2->extents.x2)
+       return PEPPER_FALSE;\r
+    
+    if (reg1->extents.y1 != reg2->extents.y1)
+       return PEPPER_FALSE;\r
+    
+    if (reg1->extents.y2 != reg2->extents.y2)
+       return PEPPER_FALSE;\r
+    
+    if (PIXREGION_NUMRECTS (reg1) != PIXREGION_NUMRECTS (reg2))
+       return PEPPER_FALSE;\r
+
+    rects1 = PIXREGION_RECTS (reg1);
+    rects2 = PIXREGION_RECTS (reg2);
+    
+    for (i = 0; i != PIXREGION_NUMRECTS (reg1); i++)
+    {
+       if (rects1[i].x1 != rects2[i].x1)
+           return PEPPER_FALSE;\r
+       
+       if (rects1[i].x2 != rects2[i].x2)
+           return PEPPER_FALSE;\r
+       
+       if (rects1[i].y1 != rects2[i].y1)
+           return PEPPER_FALSE;\r
+       
+       if (rects1[i].y2 != rects2[i].y2)
+           return PEPPER_FALSE;\r
+    }
+
+    return PEPPER_TRUE;\r
+}
+
+PEPPER_API int\r
+pepper_region_print (pepper_region_t *rgn)\r
+{
+    int num, size;
+    int i;
+    pepper_box_t * rects;\r
+
+    num = PIXREGION_NUMRECTS (rgn);
+    size = PIXREGION_SIZE (rgn);
+    rects = PIXREGION_RECTS (rgn);
+
+    fprintf (stderr, "num: %d size: %d\n", num, size);
+    fprintf (stderr, "extents: %d %d %d %d\n",
+             rgn->extents.x1,
+            rgn->extents.y1,
+            rgn->extents.x2,
+            rgn->extents.y2);
+    
+    for (i = 0; i < num; i++)
+    {
+       fprintf (stderr, "%d %d %d %d \n",
+                rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2);
+    }
+    
+    fprintf (stderr, "\n");
+
+    return(num);
+}
+
+
+PEPPER_API void\r
+pepper_region_init (pepper_region_t *region)\r
+{
+    region->extents = *pepper_region_empty_box;\r
+    region->data = pepper_region_empty_data;\r
+}
+
+PEPPER_API void\r
+pepper_region_init_rect (pepper_region_t *     region,\r
+                     int               x,
+                    int                y,
+                    unsigned int       width,
+                    unsigned int       height)
+{
+    region->extents.x1 = x;
+    region->extents.y1 = y;
+    region->extents.x2 = x + width;
+    region->extents.y2 = y + height;
+
+    if (!GOOD_RECT (&region->extents))
+    {
+        if (BAD_RECT (&region->extents))
+            PEPPER_ERROR("%s", "Invalid rectangle passed");\r
+        pepper_region_init (region);\r
+        return;
+    }
+
+    region->data = NULL;
+}
+
+PEPPER_API void\r
+pepper_region_init_with_extents (pepper_region_t *region, pepper_box_t *extents)\r
+{
+    if (!GOOD_RECT (extents))
+    {
+        if (BAD_RECT (extents))
+            PEPPER_ERROR("%s", "Invalid rectangle passed");\r
+        pepper_region_init (region);\r
+        return;
+    }
+    region->extents = *extents;
+
+    region->data = NULL;
+}
+
+PEPPER_API void\r
+pepper_region_fini (pepper_region_t *region)\r
+{
+    GOOD (region);
+    FREE_DATA (region);
+}
+
+PEPPER_API int\r
+pepper_region_n_rects (pepper_region_t *region)\r
+{
+    return PIXREGION_NUMRECTS (region);
+}
+
+PEPPER_API pepper_box_t *\r
+pepper_region_rectangles (pepper_region_t *region,\r
+                      int               *n_rects)
+{
+    if (n_rects)
+       *n_rects = PIXREGION_NUMRECTS (region);
+
+    return PIXREGION_RECTS (region);
+}
+
+static pepper_bool_t\r
+pepper_break (pepper_region_t *region)\r
+{
+    FREE_DATA (region);
+
+    region->extents = *pepper_region_empty_box;\r
+    region->data = pepper_broken_data;\r
+
+    return PEPPER_FALSE;\r
+}
+
+static pepper_bool_t\r
+pepper_rect_alloc (pepper_region_t * region,\r
+                   int             n)
+{
+    pepper_region_data_t *data;\r
+
+    if (!region->data)
+    {
+       n++;
+       region->data = alloc_data (n);
+
+       if (!region->data)
+           return pepper_break (region);\r
+
+       region->data->numRects = 1;
+       *PIXREGION_BOXPTR (region) = region->extents;
+    }
+    else if (!region->data->size)
+    {
+       region->data = alloc_data (n);
+
+       if (!region->data)
+           return pepper_break (region);\r
+
+       region->data->numRects = 0;
+    }
+    else
+    {
+       size_t data_size;
+
+       if (n == 1)
+       {
+           n = region->data->numRects;
+           if (n > 500) /* XXX pick numbers out of a hat */
+               n = 250;
+       }
+
+       n += region->data->numRects;
+       data_size = PIXREGION_SZOF (n);
+
+       if (!data_size)
+       {
+           data = NULL;
+       }
+       else
+       {
+           data = (pepper_region_data_t *)\r
+               realloc (region->data, PIXREGION_SZOF (n));
+       }
+       
+       if (!data)
+           return pepper_break (region);\r
+       
+       region->data = data;
+    }
+    
+    region->data->size = n;
+
+    return PEPPER_TRUE;\r
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_copy (pepper_region_t *dst, pepper_region_t *src)\r
+{
+    GOOD (dst);
+    GOOD (src);
+
+    if (dst == src)
+       return PEPPER_TRUE;\r
+    
+    dst->extents = src->extents;
+
+    if (!src->data || !src->data->size)
+    {
+       FREE_DATA (dst);
+       dst->data = src->data;
+       return PEPPER_TRUE;\r
+    }
+    
+    if (!dst->data || (dst->data->size < src->data->numRects))
+    {
+       FREE_DATA (dst);
+
+       dst->data = alloc_data (src->data->numRects);
+
+       if (!dst->data)
+           return pepper_break (dst);\r
+
+       dst->data->size = src->data->numRects;
+    }
+
+    dst->data->numRects = src->data->numRects;
+
+    memmove ((char *)PIXREGION_BOXPTR (dst), (char *)PIXREGION_BOXPTR (src),
+             dst->data->numRects * sizeof(pepper_box_t));\r
+
+    return PEPPER_TRUE;\r
+}
+
+/*======================================================================
+ *         Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_coalesce --\r
+ *     Attempt to merge the boxes in the current band with those in the
+ *     previous one.  We are guaranteed that the current band extends to
+ *      the end of the rects array.  Used only by pepper_op.\r
+ *
+ * Results:
+ *     The new index for the previous band.
+ *
+ * Side Effects:
+ *     If coalescing takes place:
+ *         - rectangles in the previous band will have their y2 fields
+ *           altered.
+ *         - region->data->numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static inline int
+pepper_coalesce (pepper_region_t * region,      /* Region to coalesce           */\r
+                int             prev_start,  /* Index of start of previous band */
+                int             cur_start)   /* Index of start of current band  */
+{
+    pepper_box_t *prev_box;       /* Current box in previous band           */\r
+    pepper_box_t *cur_box;        /* Current box in current band       */\r
+    int numRects;               /* Number rectangles in both bands   */
+    int y2;                     /* Bottom of current band           */
+
+    /*
+     * Figure out how many rectangles are in the band.
+     */
+    numRects = cur_start - prev_start;
+    critical_if_fail (numRects == region->data->numRects - cur_start);
+
+    if (!numRects) return cur_start;
+
+    /*
+     * The bands may only be coalesced if the bottom of the previous
+     * matches the top scanline of the current.
+     */
+    prev_box = PIXREGION_BOX (region, prev_start);
+    cur_box = PIXREGION_BOX (region, cur_start);
+    if (prev_box->y2 != cur_box->y1) return cur_start;
+
+    /*
+     * Make sure the bands have boxes in the same places. This
+     * assumes that boxes have been added in such a way that they
+     * cover the most area possible. I.e. two boxes in a band must
+     * have some horizontal space between them.
+     */
+    y2 = cur_box->y2;
+
+    do
+    {
+       if ((prev_box->x1 != cur_box->x1) || (prev_box->x2 != cur_box->x2))
+           return (cur_start);
+       
+       prev_box++;
+       cur_box++;
+       numRects--;
+    }
+    while (numRects);
+
+    /*
+     * The bands may be merged, so set the bottom y of each box
+     * in the previous band to the bottom y of the current band.
+     */
+    numRects = cur_start - prev_start;
+    region->data->numRects -= numRects;
+
+    do
+    {
+       prev_box--;
+       prev_box->y2 = y2;
+       numRects--;
+    }
+    while (numRects);
+
+    return prev_start;
+}
+
+/* Quicky macro to avoid trivial reject procedure calls to pepper_coalesce */\r
+
+#define COALESCE(new_reg, prev_band, cur_band)                          \
+    do                                                                 \
+    {                                                                  \
+       if (cur_band - prev_band == new_reg->data->numRects - cur_band) \
+           prev_band = pepper_coalesce (new_reg, prev_band, cur_band); \\r
+       else                                                            \
+           prev_band = cur_band;                                       \
+    } while (0)
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_append_non_o --\r
+ *     Handle a non-overlapping band for the union and subtract operations.
+ *      Just adds the (top/bottom-clipped) rectangles into the region.
+ *      Doesn't have to check for subsumption or anything.
+ *
+ * Results:
+ *     None.
+ *
+ * Side Effects:
+ *     region->data->numRects is incremented and the rectangles overwritten
+ *     with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static inline pepper_bool_t\r
+pepper_region_append_non_o (pepper_region_t * region,\r
+                           pepper_box_t *    r,\r
+                           pepper_box_t *    r_end,\r
+                           int             y1,
+                           int             y2)
+{
+    pepper_box_t *next_rect;\r
+    int new_rects;
+
+    new_rects = r_end - r;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (new_rects != 0);
+
+    /* Make sure we have enough space for all rectangles to be added */
+    RECTALLOC (region, new_rects);
+    next_rect = PIXREGION_TOP (region);
+    region->data->numRects += new_rects;
+
+    do
+    {
+       critical_if_fail (r->x1 < r->x2);
+       ADDRECT (next_rect, r->x1, y1, r->x2, y2);
+       r++;
+    }
+    while (r != r_end);
+
+    return PEPPER_TRUE;\r
+}
+
+#define FIND_BAND(r, r_band_end, r_end, ry1)                        \
+    do                                                              \
+    {                                                               \
+       ry1 = r->y1;                                                 \
+       r_band_end = r + 1;                                          \
+       while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) {   \
+           r_band_end++;                                            \
+       }                                                            \
+    } while (0)
+
+#define APPEND_REGIONS(new_reg, r, r_end)                              \
+    do                                                                 \
+    {                                                                  \
+       int new_rects;                                                  \
+       if ((new_rects = r_end - r)) {                                  \
+           RECTALLOC_BAIL (new_reg, new_rects, bail);                  \
+           memmove ((char *)PIXREGION_TOP (new_reg), (char *)r,        \
+                    new_rects * sizeof(pepper_box_t));                 \\r
+           new_reg->data->numRects += new_rects;                       \
+       }                                                               \
+    } while (0)
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_op --\r
+ *     Apply an operation to two regions. Called by pepper_region_union, pepper_region_inverse,\r
+ *     pepper_region_subtract, pepper_region_intersect....  Both regions MUST have at least one\r
+ *      rectangle, and cannot be the same object.
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *     The new region is overwritten.
+ *     overlap set to PEPPER_TRUE if overlap_func ever returns PEPPER_TRUE.\r
+ *
+ * Notes:
+ *     The idea behind this function is to view the two regions as sets.
+ *     Together they cover a rectangle of area that this function divides
+ *     into horizontal bands where points are covered only by one region
+ *     or by both. For the first case, the non_overlap_func is called with
+ *     each the band and the band's upper and lower extents. For the
+ *     second, the overlap_func is called to process the entire band. It
+ *     is responsible for clipping the rectangles in the band, though
+ *     this function provides the boundaries.
+ *     At the end of each band, the new region is coalesced, if possible,
+ *     to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+typedef pepper_bool_t (*overlap_proc_ptr) (pepper_region_t *region,\r
+                                          pepper_box_t *   r1,\r
+                                          pepper_box_t *   r1_end,\r
+                                          pepper_box_t *   r2,\r
+                                          pepper_box_t *   r2_end,\r
+                                          int            y1,
+                                          int            y2);
+
+static pepper_bool_t\r
+pepper_op (pepper_region_t *  new_reg,               /* Place to store result      */\r
+          pepper_region_t *  reg1,                  /* First region in operation     */\r
+          pepper_region_t *  reg2,                  /* 2d region in operation        */\r
+          overlap_proc_ptr overlap_func,          /* Function to call for over-
+                                                   * lapping bands                 */
+          int              append_non1,           /* Append non-overlapping bands  
+                                                   * in region 1 ?
+                                                   */
+          int              append_non2            /* Append non-overlapping bands
+                                                   * in region 2 ?
+                                                   */
+    )
+{
+    pepper_box_t *r1;                 /* Pointer into first region     */\r
+    pepper_box_t *r2;                 /* Pointer into 2d region             */\r
+    pepper_box_t *r1_end;             /* End of 1st region          */\r
+    pepper_box_t *r2_end;             /* End of 2d region                   */\r
+    int ybot;                       /* Bottom of intersection       */
+    int ytop;                       /* Top of intersection          */
+    pepper_region_data_t *old_data;   /* Old data for new_reg       */\r
+    int prev_band;                  /* Index of start of
+                                    * previous band in new_reg       */
+    int cur_band;                   /* Index of start of current
+                                    * band in new_reg               */
+    pepper_box_t * r1_band_end;       /* End of current band in r1     */\r
+    pepper_box_t * r2_band_end;       /* End of current band in r2     */\r
+    int top;                        /* Top of non-overlapping band   */
+    int bot;                        /* Bottom of non-overlapping band*/
+    int r1y1;                       /* Temps for r1->y1 and r2->y1   */
+    int r2y1;
+    int new_size;
+    int numRects;
+
+    /*
+     * Break any region computed from a broken region
+     */
+    if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2))
+       return pepper_break (new_reg);\r
+
+    /*
+     * Initialization:
+     * set r1, r2, r1_end and r2_end appropriately, save the rectangles
+     * of the destination region until the end in case it's one of
+     * the two source regions, then mark the "new" region empty, allocating
+     * another array of rectangles for it to use.
+     */
+
+    r1 = PIXREGION_RECTS (reg1);
+    new_size = PIXREGION_NUMRECTS (reg1);
+    r1_end = r1 + new_size;
+
+    numRects = PIXREGION_NUMRECTS (reg2);
+    r2 = PIXREGION_RECTS (reg2);
+    r2_end = r2 + numRects;
+    
+    critical_if_fail (r1 != r1_end);
+    critical_if_fail (r2 != r2_end);
+
+    old_data = (pepper_region_data_t *)NULL;\r
+
+    if (((new_reg == reg1) && (new_size > 1)) ||
+        ((new_reg == reg2) && (numRects > 1)))
+    {
+        old_data = new_reg->data;
+        new_reg->data = pepper_region_empty_data;\r
+    }
+
+    /* guess at new size */
+    if (numRects > new_size)
+       new_size = numRects;
+
+    new_size <<= 1;
+
+    if (!new_reg->data)
+       new_reg->data = pepper_region_empty_data;\r
+    else if (new_reg->data->size)
+       new_reg->data->numRects = 0;
+
+    if (new_size > new_reg->data->size)
+    {
+        if (!pepper_rect_alloc (new_reg, new_size))\r
+        {
+            free (old_data);
+            return PEPPER_FALSE;\r
+       }
+    }
+
+    /*
+     * Initialize ybot.
+     * In the upcoming loop, ybot and ytop serve different functions depending
+     * on whether the band being handled is an overlapping or non-overlapping
+     * band.
+     *  In the case of a non-overlapping band (only one of the regions
+     * has points in the band), ybot is the bottom of the most recent
+     * intersection and thus clips the top of the rectangles in that band.
+     * ytop is the top of the next intersection between the two regions and
+     * serves to clip the bottom of the rectangles in the current band.
+     * For an overlapping band (where the two regions intersect), ytop clips
+     * the top of the rectangles of both regions and ybot clips the bottoms.
+     */
+
+    ybot = PEPPER_MIN (r1->y1, r2->y1);\r
+
+    /*
+     * prev_band serves to mark the start of the previous band so rectangles
+     * can be coalesced into larger rectangles. qv. pepper_coalesce, above.\r
+     * In the beginning, there is no previous band, so prev_band == cur_band
+     * (cur_band is set later on, of course, but the first band will always
+     * start at index 0). prev_band and cur_band must be indices because of
+     * the possible expansion, and resultant moving, of the new region's
+     * array of rectangles.
+     */
+    prev_band = 0;
+
+    do
+    {
+        /*
+        * This algorithm proceeds one source-band (as opposed to a
+        * destination band, which is determined by where the two regions
+        * intersect) at a time. r1_band_end and r2_band_end serve to mark the
+        * rectangle after the last one in the current band for their
+        * respective regions.
+        */
+        critical_if_fail (r1 != r1_end);
+        critical_if_fail (r2 != r2_end);
+
+        FIND_BAND (r1, r1_band_end, r1_end, r1y1);
+        FIND_BAND (r2, r2_band_end, r2_end, r2y1);
+
+        /*
+        * First handle the band that doesn't intersect, if any.
+        *
+        * Note that attention is restricted to one band in the
+        * non-intersecting region at once, so if a region has n
+        * bands between the current position and the next place it overlaps
+        * the other, this entire loop will be passed through n times.
+        */
+        if (r1y1 < r2y1)
+        {
+            if (append_non1)
+            {
+                top = PEPPER_MAX (r1y1, ybot);\r
+                bot = PEPPER_MIN (r1->y2, r2y1);\r
+                if (top != bot)
+                {
+                    cur_band = new_reg->data->numRects;
+                    if (!pepper_region_append_non_o (new_reg, r1, r1_band_end, top, bot))\r
+                       goto bail;
+                    COALESCE (new_reg, prev_band, cur_band);
+               }
+           }
+            ytop = r2y1;
+       }
+        else if (r2y1 < r1y1)
+        {
+            if (append_non2)
+            {
+                top = PEPPER_MAX (r2y1, ybot);\r
+                bot = PEPPER_MIN (r2->y2, r1y1);\r
+               
+                if (top != bot)
+                {
+                    cur_band = new_reg->data->numRects;
+
+                    if (!pepper_region_append_non_o (new_reg, r2, r2_band_end, top, bot))\r
+                       goto bail;
+
+                    COALESCE (new_reg, prev_band, cur_band);
+               }
+           }
+            ytop = r1y1;
+       }
+        else
+        {
+            ytop = r1y1;
+       }
+
+        /*
+        * Now see if we've hit an intersecting band. The two bands only
+        * intersect if ybot > ytop
+        */
+        ybot = PEPPER_MIN (r1->y2, r2->y2);\r
+        if (ybot > ytop)
+        {
+            cur_band = new_reg->data->numRects;
+
+            if (!(*overlap_func)(new_reg,
+                                 r1, r1_band_end,
+                                 r2, r2_band_end,
+                                 ytop, ybot))
+           {
+               goto bail;
+           }
+           
+            COALESCE (new_reg, prev_band, cur_band);
+       }
+
+        /*
+        * If we've finished with a band (y2 == ybot) we skip forward
+        * in the region to the next band.
+        */
+        if (r1->y2 == ybot)
+           r1 = r1_band_end;
+
+        if (r2->y2 == ybot)
+           r2 = r2_band_end;
+
+    }
+    while (r1 != r1_end && r2 != r2_end);
+
+    /*
+     * Deal with whichever region (if any) still has rectangles left.
+     *
+     * We only need to worry about banding and coalescing for the very first
+     * band left.  After that, we can just group all remaining boxes,
+     * regardless of how many bands, into one final append to the list.
+     */
+
+    if ((r1 != r1_end) && append_non1)
+    {
+        /* Do first non_overlap1Func call, which may be able to coalesce */
+        FIND_BAND (r1, r1_band_end, r1_end, r1y1);
+       
+        cur_band = new_reg->data->numRects;
+       
+        if (!pepper_region_append_non_o (new_reg,\r
+                                         r1, r1_band_end,
+                                         PEPPER_MAX (r1y1, ybot), r1->y2))\r
+       {
+           goto bail;
+       }
+       
+        COALESCE (new_reg, prev_band, cur_band);
+
+        /* Just append the rest of the boxes  */
+        APPEND_REGIONS (new_reg, r1_band_end, r1_end);
+    }
+    else if ((r2 != r2_end) && append_non2)
+    {
+        /* Do first non_overlap2Func call, which may be able to coalesce */
+        FIND_BAND (r2, r2_band_end, r2_end, r2y1);
+
+       cur_band = new_reg->data->numRects;
+
+        if (!pepper_region_append_non_o (new_reg,\r
+                                         r2, r2_band_end,
+                                         PEPPER_MAX (r2y1, ybot), r2->y2))\r
+       {
+           goto bail;
+       }
+
+        COALESCE (new_reg, prev_band, cur_band);
+
+        /* Append rest of boxes */
+        APPEND_REGIONS (new_reg, r2_band_end, r2_end);
+    }
+
+    free (old_data);
+
+    if (!(numRects = new_reg->data->numRects))
+    {
+        FREE_DATA (new_reg);
+        new_reg->data = pepper_region_empty_data;\r
+    }
+    else if (numRects == 1)
+    {
+        new_reg->extents = *PIXREGION_BOXPTR (new_reg);
+        FREE_DATA (new_reg);
+        new_reg->data = (pepper_region_data_t *)NULL;\r
+    }
+    else
+    {
+        DOWNSIZE (new_reg, numRects);
+    }
+
+    return PEPPER_TRUE;\r
+
+bail:
+    free (old_data);
+
+    return pepper_break (new_reg);\r
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_set_extents --\r
+ *     Reset the extents of a region to what they should be. Called by
+ *     pepper_region_subtract and pepper_region_intersect as they can't\r
+ *      figure it out along the way or do so easily, as pepper_region_union can.\r
+ *
+ * Results:
+ *     None.
+ *
+ * Side Effects:
+ *     The region's 'extents' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+pepper_set_extents (pepper_region_t *region)\r
+{
+    pepper_box_t *box, *box_end;\r
+
+    if (!region->data)
+       return;
+
+    if (!region->data->size)
+    {
+        region->extents.x2 = region->extents.x1;
+        region->extents.y2 = region->extents.y1;
+        return;
+    }
+
+    box = PIXREGION_BOXPTR (region);
+    box_end = PIXREGION_END (region);
+
+    /*
+     * Since box is the first rectangle in the region, it must have the
+     * smallest y1 and since box_end is the last rectangle in the region,
+     * it must have the largest y2, because of banding. Initialize x1 and
+     * x2 from  box and box_end, resp., as good things to initialize them
+     * to...
+     */
+    region->extents.x1 = box->x1;
+    region->extents.y1 = box->y1;
+    region->extents.x2 = box_end->x2;
+    region->extents.y2 = box_end->y2;
+
+    critical_if_fail (region->extents.y1 < region->extents.y2);
+
+    while (box <= box_end)
+    {
+        if (box->x1 < region->extents.x1)
+           region->extents.x1 = box->x1;
+        if (box->x2 > region->extents.x2)
+           region->extents.x2 = box->x2;
+        box++;
+    }
+
+    critical_if_fail (region->extents.x1 < region->extents.x2);
+}
+
+/*======================================================================
+ *         Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_intersect_o --\r
+ *     Handle an overlapping band for pepper_region_intersect.\r
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *     Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static pepper_bool_t\r
+pepper_region_intersect_o (pepper_region_t *region,\r
+                           pepper_box_t *   r1,\r
+                           pepper_box_t *   r1_end,\r
+                           pepper_box_t *   r2,\r
+                           pepper_box_t *   r2_end,\r
+                           int            y1,
+                           int            y2)
+{
+    int x1;
+    int x2;
+    pepper_box_t *        next_rect;\r
+
+    next_rect = PIXREGION_TOP (region);
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    do
+    {
+        x1 = PEPPER_MAX (r1->x1, r2->x1);\r
+        x2 = PEPPER_MIN (r1->x2, r2->x2);\r
+
+        /*
+        * If there's any overlap between the two rectangles, add that
+        * overlap to the new region.
+        */
+        if (x1 < x2)
+           NEWRECT (region, next_rect, x1, y1, x2, y2);
+
+        /*
+        * Advance the pointer(s) with the leftmost right side, since the next
+        * rectangle on that list may still overlap the other region's
+        * current rectangle.
+        */
+        if (r1->x2 == x2)
+        {
+            r1++;
+       }
+        if (r2->x2 == x2)
+        {
+            r2++;
+       }
+    }
+    while ((r1 != r1_end) && (r2 != r2_end));
+
+    return PEPPER_TRUE;\r
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_intersect (pepper_region_t *     new_reg,\r
+                     pepper_region_t *        reg1,\r
+                     pepper_region_t *        reg2)\r
+{
+    GOOD (reg1);
+    GOOD (reg2);
+    GOOD (new_reg);
+
+    /* check for trivial reject */
+    if (PIXREGION_NIL (reg1) || PIXREGION_NIL (reg2) ||
+        !EXTENTCHECK (&reg1->extents, &reg2->extents))
+    {
+        /* Covers about 20% of all cases */
+        FREE_DATA (new_reg);
+        new_reg->extents.x2 = new_reg->extents.x1;
+        new_reg->extents.y2 = new_reg->extents.y1;
+        if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2))
+        {
+            new_reg->data = pepper_broken_data;\r
+            return PEPPER_FALSE;\r
+       }
+        else
+       {
+           new_reg->data = pepper_region_empty_data;\r
+       }
+    }
+    else if (!reg1->data && !reg2->data)
+    {
+        /* Covers about 80% of cases that aren't trivially rejected */
+        new_reg->extents.x1 = PEPPER_MAX (reg1->extents.x1, reg2->extents.x1);\r
+        new_reg->extents.y1 = PEPPER_MAX (reg1->extents.y1, reg2->extents.y1);\r
+        new_reg->extents.x2 = PEPPER_MIN (reg1->extents.x2, reg2->extents.x2);\r
+        new_reg->extents.y2 = PEPPER_MIN (reg1->extents.y2, reg2->extents.y2);\r
+
+        FREE_DATA (new_reg);
+
+       new_reg->data = (pepper_region_data_t *)NULL;\r
+    }
+    else if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents))
+    {
+        return pepper_region_copy (new_reg, reg1);\r
+    }
+    else if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents))
+    {
+        return pepper_region_copy (new_reg, reg2);\r
+    }
+    else if (reg1 == reg2)
+    {
+        return pepper_region_copy (new_reg, reg1);\r
+    }
+    else
+    {
+        /* General purpose intersection */
+
+        if (!pepper_op (new_reg, reg1, reg2, pepper_region_intersect_o, PEPPER_FALSE, PEPPER_FALSE))\r
+           return PEPPER_FALSE;\r
+       
+        pepper_set_extents (new_reg);\r
+    }
+
+    GOOD (new_reg);
+    return(PEPPER_TRUE);\r
+}
+
+#define MERGERECT(r)                                                   \
+    do                                                                 \
+    {                                                                  \
+        if (r->x1 <= x2)                                               \
+       {                                                               \
+            /* Merge with current rectangle */                         \
+            if (x2 < r->x2)                                            \
+               x2 = r->x2;                                             \
+       }                                                               \
+       else                                                            \
+       {                                                               \
+            /* Add current rectangle, start new one */                 \
+            NEWRECT (region, next_rect, x1, y1, x2, y2);               \
+            x1 = r->x1;                                                        \
+            x2 = r->x2;                                                        \
+       }                                                               \
+        r++;                                                           \
+    } while (0)
+
+/*======================================================================
+ *         Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_union_o --\r
+ *     Handle an overlapping band for the union operation. Picks the
+ *     left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *     region is overwritten.
+ *     overlap is set to PEPPER_TRUE if any boxes overlap.\r
+ *
+ *-----------------------------------------------------------------------
+ */
+static pepper_bool_t\r
+pepper_region_union_o (pepper_region_t *region,\r
+                      pepper_box_t *   r1,\r
+                      pepper_box_t *   r1_end,\r
+                      pepper_box_t *   r2,\r
+                      pepper_box_t *   r2_end,\r
+                      int            y1,
+                      int            y2)
+{
+    pepper_box_t *next_rect;\r
+    int x1;            /* left and right side of current union */
+    int x2;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    next_rect = PIXREGION_TOP (region);
+
+    /* Start off current rectangle */
+    if (r1->x1 < r2->x1)
+    {
+        x1 = r1->x1;
+        x2 = r1->x2;
+        r1++;
+    }
+    else
+    {
+        x1 = r2->x1;
+        x2 = r2->x2;
+        r2++;
+    }
+    while (r1 != r1_end && r2 != r2_end)
+    {
+        if (r1->x1 < r2->x1)
+           MERGERECT (r1);
+       else
+           MERGERECT (r2);
+    }
+
+    /* Finish off whoever (if any) is left */
+    if (r1 != r1_end)
+    {
+        do
+        {
+            MERGERECT (r1);
+       }
+        while (r1 != r1_end);
+    }
+    else if (r2 != r2_end)
+    {
+        do
+        {
+            MERGERECT (r2);
+       }
+        while (r2 != r2_end);
+    }
+
+    /* Add current rectangle */
+    NEWRECT (region, next_rect, x1, y1, x2, y2);
+
+    return PEPPER_TRUE;\r
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_intersect_rect (pepper_region_t *dest,\r
+                        pepper_region_t *source,\r
+                        int x, int y,
+                        unsigned int width,
+                        unsigned int height)
+{
+    pepper_region_t region;\r
+
+    region.data = NULL;
+    region.extents.x1 = x;
+    region.extents.y1 = y;
+    region.extents.x2 = x + width;
+    region.extents.y2 = y + height;
+
+    return pepper_region_intersect (dest, source, &region);\r
+}
+
+/* Convenience function for performing union of region with a
+ * single rectangle
+ */
+PEPPER_API pepper_bool_t\r
+pepper_region_union_rect (pepper_region_t *dest,\r
+                      pepper_region_t *source,\r
+                      int            x,
+                     int            y,
+                      unsigned int   width,
+                     unsigned int   height)
+{
+    pepper_region_t region;\r
+
+    region.extents.x1 = x;
+    region.extents.y1 = y;
+    region.extents.x2 = x + width;
+    region.extents.y2 = y + height;
+
+    if (!GOOD_RECT (&region.extents))
+    {
+        if (BAD_RECT (&region.extents))
+            PEPPER_ERROR("%s", "Invalid rectangle passed");\r
+       return pepper_region_copy (dest, source);\r
+    }
+
+    region.data = NULL;
+
+    return pepper_region_union (dest, source, &region);\r
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_union (pepper_region_t *new_reg,\r
+                 pepper_region_t *reg1,\r
+                 pepper_region_t *reg2)\r
+{
+    /* Return PEPPER_TRUE if some overlap\r
+     * between reg1, reg2
+     */
+    GOOD (reg1);
+    GOOD (reg2);
+    GOOD (new_reg);
+
+    /*  checks all the simple cases */
+
+    /*
+     * Region 1 and 2 are the same
+     */
+    if (reg1 == reg2)
+        return pepper_region_copy (new_reg, reg1);\r
+
+    /*
+     * Region 1 is empty
+     */
+    if (PIXREGION_NIL (reg1))
+    {
+        if (PIXREGION_NAR (reg1))
+           return pepper_break (new_reg);\r
+
+        if (new_reg != reg2)
+           return pepper_region_copy (new_reg, reg2);\r
+
+       return PEPPER_TRUE;\r
+    }
+
+    /*
+     * Region 2 is empty
+     */
+    if (PIXREGION_NIL (reg2))
+    {
+        if (PIXREGION_NAR (reg2))
+           return pepper_break (new_reg);\r
+
+       if (new_reg != reg1)
+           return pepper_region_copy (new_reg, reg1);\r
+
+       return PEPPER_TRUE;\r
+    }
+
+    /*
+     * Region 1 completely subsumes region 2
+     */
+    if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->extents))
+    {
+        if (new_reg != reg1)
+           return pepper_region_copy (new_reg, reg1);\r
+
+       return PEPPER_TRUE;\r
+    }
+
+    /*
+     * Region 2 completely subsumes region 1
+     */
+    if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->extents))
+    {
+        if (new_reg != reg2)
+           return pepper_region_copy (new_reg, reg2);\r
+
+       return PEPPER_TRUE;\r
+    }
+
+    if (!pepper_op (new_reg, reg1, reg2, pepper_region_union_o, PEPPER_TRUE, PEPPER_TRUE))\r
+       return PEPPER_FALSE;\r
+
+    new_reg->extents.x1 = PEPPER_MIN (reg1->extents.x1, reg2->extents.x1);\r
+    new_reg->extents.y1 = PEPPER_MIN (reg1->extents.y1, reg2->extents.y1);\r
+    new_reg->extents.x2 = PEPPER_MAX (reg1->extents.x2, reg2->extents.x2);\r
+    new_reg->extents.y2 = PEPPER_MAX (reg1->extents.y2, reg2->extents.y2);\r
+    
+    GOOD (new_reg);
+
+    return PEPPER_TRUE;\r
+}
+
+/*======================================================================
+ *         Batch Rectangle Union
+ *====================================================================*/
+
+#define EXCHANGE_RECTS(a, b)   \
+    {                           \
+        pepper_box_t t;                \\r
+        t = rects[a];           \
+        rects[a] = rects[b];    \
+        rects[b] = t;           \
+    }
+
+static void
+quick_sort_rects (
+    pepper_box_t rects[],\r
+    int        numRects)
+{
+    int y1;
+    int x1;
+    int i, j;
+    pepper_box_t *r;\r
+
+    /* Always called with numRects > 1 */
+
+    do
+    {
+        if (numRects == 2)
+        {
+            if (rects[0].y1 > rects[1].y1 ||
+                (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1))
+           {
+               EXCHANGE_RECTS (0, 1);
+           }
+
+            return;
+       }
+
+        /* Choose partition element, stick in location 0 */
+        EXCHANGE_RECTS (0, numRects >> 1);
+        y1 = rects[0].y1;
+        x1 = rects[0].x1;
+
+        /* Partition array */
+        i = 0;
+        j = numRects;
+
+        do
+        {
+            r = &(rects[i]);
+            do
+            {
+                r++;
+                i++;
+           }
+           while (i != numRects && (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1)));
+
+           r = &(rects[j]);
+            do
+            {
+                r--;
+                j--;
+           }
+            while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1));
+           
+            if (i < j)
+               EXCHANGE_RECTS (i, j);
+       }
+        while (i < j);
+
+        /* Move partition element back to middle */
+        EXCHANGE_RECTS (0, j);
+
+        /* Recurse */
+        if (numRects - j - 1 > 1)
+           quick_sort_rects (&rects[j + 1], numRects - j - 1);
+
+        numRects = j;
+    }
+    while (numRects > 1);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_validate --\r
+ *
+ *      Take a ``region'' which is a non-y-x-banded random collection of
+ *      rectangles, and compute a nice region which is the union of all the
+ *      rectangles.
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *      The passed-in ``region'' may be modified.
+ *     overlap set to PEPPER_TRUE if any retangles overlapped,\r
+ *      else PEPPER_FALSE;\r
+ *
+ * Strategy:
+ *      Step 1. Sort the rectangles into ascending order with primary key y1
+ *             and secondary key x1.
+ *
+ *      Step 2. Split the rectangles into the minimum number of proper y-x
+ *             banded regions.  This may require horizontally merging
+ *             rectangles, and vertically coalescing bands.  With any luck,
+ *             this step in an identity transformation (ala the Box widget),
+ *             or a coalescing into 1 box (ala Menus).
+ *
+ *     Step 3. Merge the separate regions down to a single region by calling
+ *             pepper_region_union.  Maximize the work each pepper_region_union call does by using\r
+ *             a binary merge.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static pepper_bool_t\r
+validate (pepper_region_t * badreg)\r
+{
+    /* Descriptor for regions under construction  in Step 2. */
+    typedef struct
+    {
+        pepper_region_t reg;\r
+        int prev_band;
+        int cur_band;
+    } region_info_t;
+
+    region_info_t stack_regions[64];
+
+    int numRects;                   /* Original numRects for badreg        */
+    region_info_t *ri;              /* Array of current regions                    */
+    int num_ri;                     /* Number of entries used in ri        */
+    int size_ri;                    /* Number of entries available in ri    */
+    int i;                          /* Index into rects                            */
+    int j;                          /* Index into ri                       */
+    region_info_t *rit;             /* &ri[j]                              */
+    pepper_region_t *reg;             /* ri[j].reg                         */\r
+    pepper_box_t *box;                /* Current box in rects              */\r
+    pepper_box_t *ri_box;             /* Last box in ri[j].reg             */\r
+    pepper_region_t *hreg;            /* ri[j_half].reg                            */\r
+    pepper_bool_t ret = PEPPER_TRUE;\r
+
+    if (!badreg->data)
+    {
+        GOOD (badreg);
+        return PEPPER_TRUE;\r
+    }
+    
+    numRects = badreg->data->numRects;
+    if (!numRects)
+    {
+        if (PIXREGION_NAR (badreg))
+           return PEPPER_FALSE;\r
+        GOOD (badreg);
+        return PEPPER_TRUE;\r
+    }
+    
+    if (badreg->extents.x1 < badreg->extents.x2)
+    {
+        if ((numRects) == 1)
+        {
+            FREE_DATA (badreg);
+            badreg->data = (pepper_region_data_t *) NULL;\r
+       }
+        else
+        {
+            DOWNSIZE (badreg, numRects);
+       }
+
+        GOOD (badreg);
+
+       return PEPPER_TRUE;\r
+    }
+
+    /* Step 1: Sort the rects array into ascending (y1, x1) order */
+    quick_sort_rects (PIXREGION_BOXPTR (badreg), numRects);
+
+    /* Step 2: Scatter the sorted array into the minimum number of regions */
+
+    /* Set up the first region to be the first rectangle in badreg */
+    /* Note that step 2 code will never overflow the ri[0].reg rects array */
+    ri = stack_regions;
+    size_ri = sizeof (stack_regions) / sizeof (stack_regions[0]);
+    num_ri = 1;
+    ri[0].prev_band = 0;
+    ri[0].cur_band = 0;
+    ri[0].reg = *badreg;
+    box = PIXREGION_BOXPTR (&ri[0].reg);
+    ri[0].reg.extents = *box;
+    ri[0].reg.data->numRects = 1;
+    badreg->extents = *pepper_region_empty_box;\r
+    badreg->data = pepper_region_empty_data;\r
+
+    /* Now scatter rectangles into the minimum set of valid regions.  If the
+     * next rectangle to be added to a region would force an existing rectangle
+     * in the region to be split up in order to maintain y-x banding, just
+     * forget it.  Try the next region.  If it doesn't fit cleanly into any
+     * region, make a new one.
+     */
+
+    for (i = numRects; --i > 0;)
+    {
+        box++;
+        /* Look for a region to append box to */
+        for (j = num_ri, rit = ri; --j >= 0; rit++)
+        {
+            reg = &rit->reg;
+            ri_box = PIXREGION_END (reg);
+
+            if (box->y1 == ri_box->y1 && box->y2 == ri_box->y2)
+            {
+                /* box is in same band as ri_box.  Merge or append it */
+                if (box->x1 <= ri_box->x2)
+                {
+                    /* Merge it with ri_box */
+                    if (box->x2 > ri_box->x2)
+                       ri_box->x2 = box->x2;
+               }
+                else
+                {
+                    RECTALLOC_BAIL (reg, 1, bail);
+                    *PIXREGION_TOP (reg) = *box;
+                    reg->data->numRects++;
+               }
+               
+                goto next_rect;   /* So sue me */
+           }
+            else if (box->y1 >= ri_box->y2)
+            {
+                /* Put box into new band */
+                if (reg->extents.x2 < ri_box->x2)
+                   reg->extents.x2 = ri_box->x2;
+               
+                if (reg->extents.x1 > box->x1)
+                   reg->extents.x1 = box->x1;
+               
+                COALESCE (reg, rit->prev_band, rit->cur_band);
+                rit->cur_band = reg->data->numRects;
+                RECTALLOC_BAIL (reg, 1, bail);
+                *PIXREGION_TOP (reg) = *box;
+                reg->data->numRects++;
+
+                goto next_rect;
+           }
+            /* Well, this region was inappropriate.  Try the next one. */
+       } /* for j */
+
+        /* Uh-oh.  No regions were appropriate.  Create a new one. */
+        if (size_ri == num_ri)
+        {
+            size_t data_size;
+
+            /* Oops, allocate space for new region information */
+            size_ri <<= 1;
+
+            data_size = size_ri * sizeof(region_info_t);
+            if (data_size / size_ri != sizeof(region_info_t))
+               goto bail;
+
+            if (ri == stack_regions)
+            {
+                rit = malloc (data_size);
+                if (!rit)
+                   goto bail;
+                memcpy (rit, ri, num_ri * sizeof (region_info_t));
+           }
+            else
+            {
+                rit = (region_info_t *) realloc (ri, data_size);
+                if (!rit)
+                   goto bail;
+           }
+            ri = rit;
+            rit = &ri[num_ri];
+       }
+        num_ri++;
+        rit->prev_band = 0;
+        rit->cur_band = 0;
+        rit->reg.extents = *box;
+        rit->reg.data = (pepper_region_data_t *)NULL;\r
+
+       /* MUST force allocation */
+        if (!pepper_rect_alloc (&rit->reg, (i + num_ri) / num_ri))\r
+           goto bail;
+       
+    next_rect: ;
+    } /* for i */
+
+    /* Make a final pass over each region in order to COALESCE and set
+     * extents.x2 and extents.y2
+     */
+    for (j = num_ri, rit = ri; --j >= 0; rit++)
+    {
+        reg = &rit->reg;
+        ri_box = PIXREGION_END (reg);
+        reg->extents.y2 = ri_box->y2;
+
+        if (reg->extents.x2 < ri_box->x2)
+           reg->extents.x2 = ri_box->x2;
+       
+        COALESCE (reg, rit->prev_band, rit->cur_band);
+
+       if (reg->data->numRects == 1) /* keep unions happy below */
+        {
+            FREE_DATA (reg);
+            reg->data = (pepper_region_data_t *)NULL;\r
+       }
+    }
+
+    /* Step 3: Union all regions into a single region */
+    while (num_ri > 1)
+    {
+        int half = num_ri / 2;
+        for (j = num_ri & 1; j < (half + (num_ri & 1)); j++)
+        {
+            reg = &ri[j].reg;
+            hreg = &ri[j + half].reg;
+
+            if (!pepper_op (reg, reg, hreg, pepper_region_union_o, PEPPER_TRUE, PEPPER_TRUE))\r
+               ret = PEPPER_FALSE;\r
+
+            if (hreg->extents.x1 < reg->extents.x1)
+               reg->extents.x1 = hreg->extents.x1;
+
+            if (hreg->extents.y1 < reg->extents.y1)
+               reg->extents.y1 = hreg->extents.y1;
+
+            if (hreg->extents.x2 > reg->extents.x2)
+               reg->extents.x2 = hreg->extents.x2;
+
+            if (hreg->extents.y2 > reg->extents.y2)
+               reg->extents.y2 = hreg->extents.y2;
+
+            FREE_DATA (hreg);
+       }
+
+        num_ri -= half;
+
+       if (!ret)
+           goto bail;
+    }
+
+    *badreg = ri[0].reg;
+
+    if (ri != stack_regions)
+       free (ri);
+
+    GOOD (badreg);
+    return ret;
+
+bail:
+    for (i = 0; i < num_ri; i++)
+       FREE_DATA (&ri[i].reg);
+
+    if (ri != stack_regions)
+       free (ri);
+
+    return pepper_break (badreg);\r
+}
+
+/*======================================================================
+ *                Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_subtract_o --\r
+ *     Overlapping band subtraction. x1 is the left-most point not yet
+ *     checked.
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *     region may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static pepper_bool_t\r
+pepper_region_subtract_o (pepper_region_t * region,\r
+                         pepper_box_t *    r1,\r
+                         pepper_box_t *    r1_end,\r
+                         pepper_box_t *    r2,\r
+                         pepper_box_t *    r2_end,\r
+                         int             y1,
+                         int             y2)
+{
+    pepper_box_t *        next_rect;\r
+    int x1;
+
+    x1 = r1->x1;
+
+    critical_if_fail (y1 < y2);
+    critical_if_fail (r1 != r1_end && r2 != r2_end);
+
+    next_rect = PIXREGION_TOP (region);
+
+    do
+    {
+        if (r2->x2 <= x1)
+        {
+            /*
+            * Subtrahend entirely to left of minuend: go to next subtrahend.
+            */
+            r2++;
+       }
+        else if (r2->x1 <= x1)
+        {
+            /*
+            * Subtrahend precedes minuend: nuke left edge of minuend.
+            */
+            x1 = r2->x2;
+            if (x1 >= r1->x2)
+            {
+                /*
+                * Minuend completely covered: advance to next minuend and
+                * reset left fence to edge of new minuend.
+                */
+                r1++;
+                if (r1 != r1_end)
+                   x1 = r1->x1;
+           }
+            else
+            {
+                /*
+                * Subtrahend now used up since it doesn't extend beyond
+                * minuend
+                */
+                r2++;
+           }
+       }
+        else if (r2->x1 < r1->x2)
+        {
+            /*
+            * Left part of subtrahend covers part of minuend: add uncovered
+            * part of minuend to region and skip to next subtrahend.
+            */
+            critical_if_fail (x1 < r2->x1);
+            NEWRECT (region, next_rect, x1, y1, r2->x1, y2);
+
+            x1 = r2->x2;
+            if (x1 >= r1->x2)
+            {
+                /*
+                * Minuend used up: advance to new...
+                */
+                r1++;
+                if (r1 != r1_end)
+                   x1 = r1->x1;
+           }
+            else
+            {
+                /*
+                * Subtrahend used up
+                */
+                r2++;
+           }
+       }
+        else
+        {
+            /*
+            * Minuend used up: add any remaining piece before advancing.
+            */
+            if (r1->x2 > x1)
+               NEWRECT (region, next_rect, x1, y1, r1->x2, y2);
+
+            r1++;
+
+           if (r1 != r1_end)
+               x1 = r1->x1;
+       }
+    }
+    while ((r1 != r1_end) && (r2 != r2_end));
+
+    /*
+     * Add remaining minuend rectangles to region.
+     */
+    while (r1 != r1_end)
+    {
+        critical_if_fail (x1 < r1->x2);
+
+        NEWRECT (region, next_rect, x1, y1, r1->x2, y2);
+
+        r1++;
+        if (r1 != r1_end)
+           x1 = r1->x1;
+    }
+    return PEPPER_TRUE;\r
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_subtract --\r
+ *     Subtract reg_s from reg_m and leave the result in reg_d.
+ *     S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Results:
+ *     PEPPER_TRUE if successful.\r
+ *
+ * Side Effects:
+ *     reg_d is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+PEPPER_API pepper_bool_t\r
+pepper_region_subtract (pepper_region_t *reg_d,\r
+                    pepper_region_t *reg_m,\r
+                    pepper_region_t *reg_s)\r
+{
+    GOOD (reg_m);
+    GOOD (reg_s);
+    GOOD (reg_d);
+    
+    /* check for trivial rejects */
+    if (PIXREGION_NIL (reg_m) || PIXREGION_NIL (reg_s) ||
+        !EXTENTCHECK (&reg_m->extents, &reg_s->extents))
+    {
+        if (PIXREGION_NAR (reg_s))
+           return pepper_break (reg_d);\r
+       
+        return pepper_region_copy (reg_d, reg_m);\r
+    }
+    else if (reg_m == reg_s)
+    {
+        FREE_DATA (reg_d);
+        reg_d->extents.x2 = reg_d->extents.x1;
+        reg_d->extents.y2 = reg_d->extents.y1;
+        reg_d->data = pepper_region_empty_data;\r
+
+        return PEPPER_TRUE;\r
+    }
+
+    /* Add those rectangles in region 1 that aren't in region 2,
+       do yucky subtraction for overlaps, and
+       just throw away rectangles in region 2 that aren't in region 1 */
+    if (!pepper_op (reg_d, reg_m, reg_s, pepper_region_subtract_o, PEPPER_TRUE, PEPPER_FALSE))\r
+       return PEPPER_FALSE;\r
+
+    /*
+     * Can't alter reg_d's extents before we call pepper_op because\r
+     * it might be one of the source regions and pepper_op depends\r
+     * on the extents of those regions being unaltered. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    pepper_set_extents (reg_d);\r
+    GOOD (reg_d);
+    return PEPPER_TRUE;\r
+}
+
+/*======================================================================
+ *         Region Inversion
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * pepper_region_inverse --\r
+ *     Take a region and a box and return a region that is everything
+ *     in the box but not in the region. The careful reader will note
+ *     that this is the same as subtracting the region from the box...
+ *
+ * Results:
+ *     PEPPER_TRUE.\r
+ *
+ * Side Effects:
+ *     new_reg is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+PEPPER_API pepper_bool_t\r
+pepper_region_inverse (pepper_region_t *new_reg,  /* Destination region */\r
+                  pepper_region_t *reg1,     /* Region to invert */\r
+                  pepper_box_t *   inv_rect) /* Bounding box for inversion */\r
+{
+    pepper_region_t inv_reg; /* Quick and dirty region made from the\r
+                           * bounding box */
+    GOOD (reg1);
+    GOOD (new_reg);
+    
+    /* check for trivial rejects */
+    if (PIXREGION_NIL (reg1) || !EXTENTCHECK (inv_rect, &reg1->extents))
+    {
+        if (PIXREGION_NAR (reg1))
+           return pepper_break (new_reg);\r
+       
+        new_reg->extents = *inv_rect;
+        FREE_DATA (new_reg);
+        new_reg->data = (pepper_region_data_t *)NULL;\r
+       
+        return PEPPER_TRUE;\r
+    }
+
+    /* Add those rectangles in region 1 that aren't in region 2,
+     * do yucky subtraction for overlaps, and
+     * just throw away rectangles in region 2 that aren't in region 1
+     */
+    inv_reg.extents = *inv_rect;
+    inv_reg.data = (pepper_region_data_t *)NULL;\r
+    if (!pepper_op (new_reg, &inv_reg, reg1, pepper_region_subtract_o, PEPPER_TRUE, PEPPER_FALSE))\r
+       return PEPPER_FALSE;\r
+
+    /*
+     * Can't alter new_reg's extents before we call pepper_op because\r
+     * it might be one of the source regions and pepper_op depends\r
+     * on the extents of those regions being unaltered. Besides, this
+     * way there's no checking against rectangles that will be nuked
+     * due to coalescing, so we have to examine fewer rectangles.
+     */
+    pepper_set_extents (new_reg);\r
+    GOOD (new_reg);
+    return PEPPER_TRUE;\r
+}
+
+/* In time O(log n), locate the first box whose y2 is greater than y.
+ * Return @end if no such box exists.
+ */
+static pepper_box_t *\r
+find_box_for_y (pepper_box_t *begin, pepper_box_t *end, int y)\r
+{
+    pepper_box_t *mid;\r
+
+    if (end == begin)
+       return end;
+
+    if (end - begin == 1)
+    {
+       if (begin->y2 > y)
+           return begin;
+       else
+           return end;
+    }
+
+    mid = begin + (end - begin) / 2;
+    if (mid->y2 > y)
+    {
+       /* If no box is found in [begin, mid], the function
+        * will return @mid, which is then known to be the
+        * correct answer.
+        */
+       return find_box_for_y (begin, mid, y);
+    }
+    else
+    {
+       return find_box_for_y (mid, end, y);
+    }
+}
+
+/*
+ *   rect_in(region, rect)
+ *   This routine takes a pointer to a region and a pointer to a box
+ *   and determines if the box is outside/inside/partly inside the region.
+ *
+ *   The idea is to travel through the list of rectangles trying to cover the
+ *   passed box with them. Anytime a piece of the rectangle isn't covered
+ *   by a band of rectangles, part_out is set PEPPER_TRUE. Any time a rectangle in\r
+ *   the region covers part of the box, part_in is set PEPPER_TRUE. The process ends\r
+ *   when either the box has been completely covered (we reached a band that
+ *   doesn't overlap the box, part_in is PEPPER_TRUE and part_out is false), the\r
+ *   box has been partially covered (part_in == part_out == PEPPER_TRUE -- because of\r
+ *   the banding, the first time this is true we know the box is only
+ *   partially in the region) or is outside the region (we reached a band
+ *   that doesn't overlap the box at all and part_in is false)
+ */
+PEPPER_API pepper_region_overlap_t\r
+pepper_region_contains_rectangle (pepper_region_t *  region,\r
+                             pepper_box_t *     prect)\r
+{
+    pepper_box_t *     pbox;\r
+    pepper_box_t *     pbox_end;\r
+    int part_in, part_out;
+    int numRects;
+    int x, y;
+
+    GOOD (region);
+
+    numRects = PIXREGION_NUMRECTS (region);
+
+    /* useful optimization */
+    if (!numRects || !EXTENTCHECK (&region->extents, prect))
+       return(PEPPER_REGION_OUT);\r
+
+    if (numRects == 1)
+    {
+        /* We know that it must be PEPPER_REGION_IN or PEPPER_REGION_PART */\r
+        if (SUBSUMES (&region->extents, prect))
+           return(PEPPER_REGION_IN);\r
+        else
+           return(PEPPER_REGION_PART);\r
+    }
+
+    part_out = PEPPER_FALSE;\r
+    part_in = PEPPER_FALSE;\r
+
+    /* (x,y) starts at upper left of rect, moving to the right and down */
+    x = prect->x1;
+    y = prect->y1;
+
+    /* can stop when both part_out and part_in are PEPPER_TRUE, or we reach prect->y2 */\r
+    for (pbox = PIXREGION_BOXPTR (region), pbox_end = pbox + numRects;
+        pbox != pbox_end;
+        pbox++)
+    {
+       /* getting up to speed or skipping remainder of band */
+       if (pbox->y2 <= y)
+       {
+           if ((pbox = find_box_for_y (pbox, pbox_end, y)) == pbox_end)
+               break;
+       }
+
+        if (pbox->y1 > y)
+        {
+            part_out = PEPPER_TRUE;     /* missed part of rectangle above */\r
+            if (part_in || (pbox->y1 >= prect->y2))
+               break;
+            y = pbox->y1;       /* x guaranteed to be == prect->x1 */
+       }
+
+        if (pbox->x2 <= x)
+           continue;           /* not far enough over yet */
+
+        if (pbox->x1 > x)
+        {
+            part_out = PEPPER_TRUE;     /* missed part of rectangle to left */\r
+            if (part_in)
+               break;
+       }
+
+        if (pbox->x1 < prect->x2)
+        {
+            part_in = PEPPER_TRUE;      /* definitely overlap */\r
+            if (part_out)
+               break;
+       }
+
+        if (pbox->x2 >= prect->x2)
+        {
+            y = pbox->y2;       /* finished with this band */
+            if (y >= prect->y2)
+               break;
+            x = prect->x1;      /* reset x out to left again */
+       }
+        else
+        {
+            /*
+            * Because boxes in a band are maximal width, if the first box
+            * to overlap the rectangle doesn't completely cover it in that
+            * band, the rectangle must be partially out, since some of it
+            * will be uncovered in that band. part_in will have been set true
+            * by now...
+            */
+            part_out = PEPPER_TRUE;\r
+            break;
+       }
+    }
+
+    if (part_in)
+    {
+        if (y < prect->y2)
+           return PEPPER_REGION_PART;\r
+        else
+           return PEPPER_REGION_IN;\r
+    }
+    else
+    {
+        return PEPPER_REGION_OUT;\r
+    }
+}
+
+/* pepper_region_translate (region, x, y)\r
+ * translates in place
+ */
+
+PEPPER_API void\r
+pepper_region_translate (pepper_region_t *region, int x, int y)\r
+{
+    overflow_int_t x1, x2, y1, y2;
+    int nbox;
+    pepper_box_t * pbox;\r
+
+    GOOD (region);
+    region->extents.x1 = x1 = region->extents.x1 + x;
+    region->extents.y1 = y1 = region->extents.y1 + y;
+    region->extents.x2 = x2 = region->extents.x2 + x;
+    region->extents.y2 = y2 = region->extents.y2 + y;
+    
+    if (((x1 - INT32_MIN) | (y1 - INT32_MIN) | (INT32_MAX - x2) | (INT32_MAX - y2)) >= 0)\r
+    {
+        if (region->data && (nbox = region->data->numRects))
+        {
+            for (pbox = PIXREGION_BOXPTR (region); nbox--; pbox++)
+            {
+                pbox->x1 += x;
+                pbox->y1 += y;
+                pbox->x2 += x;
+                pbox->y2 += y;
+           }
+       }
+        return;
+    }
+
+    if (((x2 - INT32_MIN) | (y2 - INT32_MIN) | (INT32_MAX - x1) | (INT32_MAX - y1)) <= 0)\r
+    {
+        region->extents.x2 = region->extents.x1;
+        region->extents.y2 = region->extents.y1;
+        FREE_DATA (region);
+        region->data = pepper_region_empty_data;\r
+        return;
+    }
+
+    if (x1 < INT32_MIN)\r
+       region->extents.x1 = INT32_MIN;\r
+    else if (x2 > INT32_MAX)\r
+       region->extents.x2 = INT32_MAX;\r
+
+    if (y1 < INT32_MIN)\r
+       region->extents.y1 = INT32_MIN;\r
+    else if (y2 > INT32_MAX)\r
+       region->extents.y2 = INT32_MAX;\r
+
+    if (region->data && (nbox = region->data->numRects))
+    {
+        pepper_box_t * pbox_out;\r
+
+        for (pbox_out = pbox = PIXREGION_BOXPTR (region); nbox--; pbox++)
+        {
+            pbox_out->x1 = x1 = pbox->x1 + x;
+            pbox_out->y1 = y1 = pbox->y1 + y;
+            pbox_out->x2 = x2 = pbox->x2 + x;
+            pbox_out->y2 = y2 = pbox->y2 + y;
+
+            if (((x2 - INT32_MIN) | (y2 - INT32_MIN) |\r
+                 (INT32_MAX - x1) | (INT32_MAX - y1)) <= 0)\r
+            {
+                region->data->numRects--;
+                continue;
+           }
+
+            if (x1 < INT32_MIN)\r
+               pbox_out->x1 = INT32_MIN;\r
+            else if (x2 > INT32_MAX)\r
+               pbox_out->x2 = INT32_MAX;\r
+
+            if (y1 < INT32_MIN)\r
+               pbox_out->y1 = INT32_MIN;\r
+            else if (y2 > INT32_MAX)\r
+               pbox_out->y2 = INT32_MAX;\r
+
+            pbox_out++;
+       }
+
+        if (pbox_out != pbox)
+        {
+            if (region->data->numRects == 1)
+            {
+                region->extents = *PIXREGION_BOXPTR (region);
+                FREE_DATA (region);
+                region->data = (pepper_region_data_t *)NULL;\r
+           }
+            else
+           {
+               pepper_set_extents (region);\r
+           }
+       }
+    }
+
+    GOOD (region);
+}
+
+PEPPER_API void\r
+pepper_region_reset (pepper_region_t *region, pepper_box_t *box)\r
+{
+    GOOD (region);
+
+    critical_if_fail (GOOD_RECT (box));
+
+    region->extents = *box;
+
+    FREE_DATA (region);
+
+    region->data = NULL;
+}
+
+PEPPER_API void\r
+pepper_region_clear (pepper_region_t *region)\r
+{
+    GOOD (region);
+    FREE_DATA (region);
+
+    region->extents = *pepper_region_empty_box;\r
+    region->data = pepper_region_empty_data;\r
+}
+
+/* box is "return" value */
+PEPPER_API pepper_bool_t\r
+pepper_region_contains_point (pepper_region_t * region,\r
+                          int x, int y,
+                          pepper_box_t * box)\r
+{
+    pepper_box_t *pbox, *pbox_end;\r
+    int numRects;
+
+    GOOD (region);
+    numRects = PIXREGION_NUMRECTS (region);
+
+    if (!numRects || !INBOX (&region->extents, x, y))
+       return(PEPPER_FALSE);\r
+
+    if (numRects == 1)
+    {
+        if (box)
+           *box = region->extents;
+
+        return(PEPPER_TRUE);\r
+    }
+
+    pbox = PIXREGION_BOXPTR (region);
+    pbox_end = pbox + numRects;
+
+    pbox = find_box_for_y (pbox, pbox_end, y);
+
+    for (;pbox != pbox_end; pbox++)
+    {
+        if ((y < pbox->y1) || (x < pbox->x1))
+           break;              /* missed it */
+
+        if (x >= pbox->x2)
+           continue;           /* not there yet */
+
+        if (box)
+           *box = *pbox;
+
+        return(PEPPER_TRUE);\r
+    }
+
+    return(PEPPER_FALSE);\r
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_not_empty (pepper_region_t * region)\r
+{
+    GOOD (region);
+
+    return(!PIXREGION_NIL (region));
+}
+
+PEPPER_API pepper_box_t *\r
+pepper_region_extents (pepper_region_t * region)\r
+{
+    GOOD (region);
+
+    return(&region->extents);
+}
+
+/*
+ * Clip a list of scanlines to a region.  The caller has allocated the
+ * space.  FSorted is non-zero if the scanline origins are in ascending order.
+ *
+ * returns the number of new, clipped scanlines.
+ */
+
+PEPPER_API pepper_bool_t\r
+pepper_region_selfcheck (pepper_region_t *reg)\r
+{
+    int i, numRects;
+
+    if ((reg->extents.x1 > reg->extents.x2) ||
+        (reg->extents.y1 > reg->extents.y2))
+    {
+       return PEPPER_FALSE;\r
+    }
+
+    numRects = PIXREGION_NUMRECTS (reg);
+    if (!numRects)
+    {
+       return ((reg->extents.x1 == reg->extents.x2) &&
+               (reg->extents.y1 == reg->extents.y2) &&
+               (reg->data->size || (reg->data == pepper_region_empty_data)));\r
+    }
+    else if (numRects == 1)
+    {
+       return (!reg->data);
+    }
+    else
+    {
+        pepper_box_t * pbox_p, * pbox_n;\r
+        pepper_box_t box;\r
+
+        pbox_p = PIXREGION_RECTS (reg);
+        box = *pbox_p;
+        box.y2 = pbox_p[numRects - 1].y2;
+        pbox_n = pbox_p + 1;
+
+        for (i = numRects; --i > 0; pbox_p++, pbox_n++)
+        {
+            if ((pbox_n->x1 >= pbox_n->x2) ||
+                (pbox_n->y1 >= pbox_n->y2))
+           {
+               return PEPPER_FALSE;\r
+           }
+
+            if (pbox_n->x1 < box.x1)
+               box.x1 = pbox_n->x1;
+           
+            if (pbox_n->x2 > box.x2)
+               box.x2 = pbox_n->x2;
+           
+            if ((pbox_n->y1 < pbox_p->y1) ||
+                ((pbox_n->y1 == pbox_p->y1) &&
+                 ((pbox_n->x1 < pbox_p->x2) || (pbox_n->y2 != pbox_p->y2))))
+           {
+               return PEPPER_FALSE;\r
+           }
+       }
+
+        return ((box.x1 == reg->extents.x1) &&
+                (box.x2 == reg->extents.x2) &&
+                (box.y1 == reg->extents.y1) &&
+                (box.y2 == reg->extents.y2));
+    }
+}
+
+PEPPER_API pepper_bool_t\r
+pepper_region_init_rects (pepper_region_t *region,\r
+                      const pepper_box_t *boxes, int count)\r
+{
+    pepper_box_t *rects;\r
+    int displacement;
+    int i;
+
+    /* if it's 1, then we just want to set the extents, so call
+     * the existing method. */
+    if (count == 1)
+    {
+        pepper_region_init_rect (region,\r
+                             boxes[0].x1,
+                             boxes[0].y1,
+                             boxes[0].x2 - boxes[0].x1,
+                             boxes[0].y2 - boxes[0].y1);
+        return PEPPER_TRUE;\r
+    }
+
+    pepper_region_init (region);\r
+
+    /* if it's 0, don't call pepper_rect_alloc -- 0 rectangles is\r
+     * a special case, and causing pepper_rect_alloc would cause\r
+     * us to leak memory (because the 0-rect case should be the
+     * static pepper_region_empty_data data).\r
+     */
+    if (count == 0)
+       return PEPPER_TRUE;\r
+
+    if (!pepper_rect_alloc (region, count))\r
+       return PEPPER_FALSE;\r
+
+    rects = PIXREGION_RECTS (region);
+
+    /* Copy in the rects */
+    memcpy (rects, boxes, sizeof(pepper_box_t) * count);\r
+    region->data->numRects = count;
+
+    /* Eliminate empty and malformed rectangles */
+    displacement = 0;
+
+    for (i = 0; i < count; ++i)
+    {
+        pepper_box_t *box = &rects[i];\r
+
+        if (box->x1 >= box->x2 || box->y1 >= box->y2)
+           displacement++;
+        else if (displacement)
+           rects[i - displacement] = rects[i];
+    }
+
+    region->data->numRects -= displacement;
+
+    /* If eliminating empty rectangles caused there
+     * to be only 0 or 1 rectangles, deal with that.
+     */
+    if (region->data->numRects == 0)
+    {
+        FREE_DATA (region);
+        pepper_region_init (region);\r
+
+        return PEPPER_TRUE;\r
+    }
+
+    if (region->data->numRects == 1)
+    {
+        region->extents = rects[0];
+
+        FREE_DATA (region);
+        region->data = NULL;
+
+        GOOD (region);
+
+        return PEPPER_TRUE;\r
+    }
+
+    /* Validate */
+    region->extents.x1 = region->extents.x2 = 0;
+
+    return validate (region);
+}\r