[Mono-patches] r58520 - trunk/libgdiplus/src

Sebastien Pouliot (sebastien at ximian.com) mono-patches-list at lists.ximian.com
Sat Mar 25 11:33:38 EST 2006


Author: spouliot
Date: 2006-03-25 11:33:37 -0500 (Sat, 25 Mar 2006)
New Revision: 58520

Added:
   trunk/libgdiplus/src/region-bitmap.c
   trunk/libgdiplus/src/region-bitmap.h
   trunk/libgdiplus/src/region-path-tree.c
   trunk/libgdiplus/src/region-path-tree.h
   trunk/libgdiplus/src/region.h
Modified:
   trunk/libgdiplus/src/ChangeLog
   trunk/libgdiplus/src/Makefile.am
   trunk/libgdiplus/src/gdip.h
   trunk/libgdiplus/src/gdipImage.h
   trunk/libgdiplus/src/graphics.c
   trunk/libgdiplus/src/region.c
Log:
2006-03-25  Sebastien Pouliot  <sebastien at ximian.com> 

	* gdip.h: Added new structures required for path tree (GpPathTree) and
	regions bitmaps (GpRegionBitmap).
	* gdipImage.h: Added prototype for GdipGetImageGraphicsContext.
	* graphics.c: Re-implement GdipFillRegion to use the alpha bitmap
	provided for by region-bitmap. Correct clipping wrt infinite regions.
	* region.c: Adjust existing API to use the bitmap regions for path 
	based regions, while keeping the existing code for rectangular based
	regions.
	* region.h: New. Header file for private region functions.
	* region-bitmap.c|h: New. Code to generate bitmap for regions and the
	binary operators on them (union, intersection, exclude, complement, 
	xor).
	* region-path-tree.c|h: New. Code to create and manipulate trees of
	path with binary operators. This allows to re-create any region from
	a small set of serialized data.
	* Makefile.am: Add the new files to the build.



Modified: trunk/libgdiplus/src/ChangeLog
===================================================================
--- trunk/libgdiplus/src/ChangeLog	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/ChangeLog	2006-03-25 16:33:37 UTC (rev 58520)
@@ -1,3 +1,22 @@
+2006-03-25  Sebastien Pouliot  <sebastien at ximian.com> 
+
+	* gdip.h: Added new structures required for path tree (GpPathTree) and
+	regions bitmaps (GpRegionBitmap).
+	* gdipImage.h: Added prototype for GdipGetImageGraphicsContext.
+	* graphics.c: Re-implement GdipFillRegion to use the alpha bitmap
+	provided for by region-bitmap. Correct clipping wrt infinite regions.
+	* region.c: Adjust existing API to use the bitmap regions for path 
+	based regions, while keeping the existing code for rectangular based
+	regions.
+	* region.h: New. Header file for private region functions.
+	* region-bitmap.c|h: New. Code to generate bitmap for regions and the
+	binary operators on them (union, intersection, exclude, complement, 
+	xor).
+	* region-path-tree.c|h: New. Code to create and manipulate trees of
+	path with binary operators. This allows to re-create any region from
+	a small set of serialized data.
+	* Makefile.am: Add the new files to the build.
+
 2006-03-23  Peter Dennis Bartok  <pbartok at novell.com>
 
 	* graphics.c (MeasureOrDrawString): Avoid trying to draw 0-length 

Modified: trunk/libgdiplus/src/Makefile.am
===================================================================
--- trunk/libgdiplus/src/Makefile.am	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/Makefile.am	2006-03-25 16:33:37 UTC (rev 58520)
@@ -25,6 +25,10 @@
 	pathgradientbrush.c \
 	pathgradientbrush.h \
 	region.c	\
+	region-bitmap.c	\
+	region-bitmap.h	\
+	region-path-tree.c	\
+	region-path-tree.h	\
 	solidbrush.c	\
 	solidbrush.h	\
 	stringformat.c	\

Modified: trunk/libgdiplus/src/gdip.h
===================================================================
--- trunk/libgdiplus/src/gdip.h	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/gdip.h	2006-03-25 16:33:37 UTC (rev 58520)
@@ -11,7 +11,7 @@
  *	Geoff Norton (gnorton at customerdna.com)
  *      Jonathan Gilbert (logic at deltaq.org)
  *
- * Copyright (C) Novell, Inc. 2003-2005. http://www.novell.com
+ * Copyright (C) 2003-2006 Novell, Inc (http://www.novell.com)
  */
 
 #ifndef _GDIP_H
@@ -720,11 +720,30 @@
 	int pathTypePosition; /* The position to get the next path type inside a subpath */
 } GpPathIterator;
 
+/* internal (private) structure */
 typedef struct {
+	int X;
+	int Y;
+	int Width;
+	int Height;
+	unsigned char *Mask;
+	BOOL reduced;
+} GpRegionBitmap;
+
+/* internal (private) structure */
+typedef struct GpPathTree {
+	CombineMode	mode;
+	GpPath*		path;
+	struct GpPathTree*	branch1;
+	struct GpPathTree*	branch2;
+} GpPathTree;
+
+typedef struct {
 	guint32		type;
         int		cnt;
         GpRectF*	rects;
-	GpPath*		path;
+	GpPathTree*	tree;
+	GpRegionBitmap*	bitmap;
 } GpRegion;
  
 typedef struct {

Modified: trunk/libgdiplus/src/gdipImage.h
===================================================================
--- trunk/libgdiplus/src/gdipImage.h	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/gdipImage.h	2006-03-25 16:33:37 UTC (rev 58520)
@@ -4,7 +4,7 @@
  * Authors:
  *  	Sanjay Gupta (gsanjay at novell.com)
  *
- * Copyright (C) Novell, Inc. 2003-2004.
+ * Copyright (C) 2003-2006 Novell, Inc (http://www.novell.com)
  */
 
 #ifndef _GDIPIMAGE_H
@@ -98,6 +98,7 @@
 GpStatus GdipImageGetFrameCount (GpImage *image, GDIPCONST GUID *dimensionGUID, UINT* count); 
 GpStatus GdipImageSelectActiveFrame (GpImage *image, GDIPCONST GUID *dimensionGUID, UINT index);
 GpStatus GdipImageRotateFlip (GpImage *image, RotateFlipType type);
+GpStatus GdipGetImageGraphicsContext (GpImage *image, GpGraphics **graphics);
 GpStatus GdipGetImagePalette (GpImage *image, ColorPalette *palette, int size);
 GpStatus GdipSetImagePalette (GpImage *image, GDIPCONST ColorPalette *palette);
 GpStatus GdipGetImagePaletteSize (GpImage *image, int* size);

Modified: trunk/libgdiplus/src/graphics.c
===================================================================
--- trunk/libgdiplus/src/graphics.c	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/graphics.c	2006-03-25 16:33:37 UTC (rev 58520)
@@ -26,17 +26,15 @@
  */
 
 #include "gdip.h"
+#include "gdipImage.h"
+#include "region.h"
 #include "brush.h"
 #include <math.h>
 #include <glib.h>
 
-extern BOOL gdip_is_Point_in_RectF_inclusive (float x, float y, GpRectF* rect);
-extern BOOL gdip_is_Point_in_RectF_inclusive (float x, float y, GpRectF* rect);
 extern FT_Face gdip_cairo_ft_font_lock_face (cairo_font_face_t *cairofnt);
 extern void gdip_cairo_ft_font_unlock_face (cairo_font_face_t *cairofnt);
 void gdip_set_cairo_clipping (GpGraphics *graphics);
-extern void gdip_clear_region (GpRegion *region);
-extern void gdip_copy_region (GpRegion *source, GpRegion *dest);
 
 extern cairo_filter_t gdip_get_cairo_filter (InterpolationMode imode);
 
@@ -1854,15 +1852,53 @@
         if (!graphics || !brush || !region)
 		return InvalidParameter;
 
-	/* a region is either a complex path */
+	/* if this is a region with a complex path */
 	if (region->type == RegionTypePath) {
-		if (!region->path || (region->path->count == 0))
+		GpStatus status;
+		GpBitmap *bitmap;
+
+		/* (optimization) if if the path is empty, return immediately */
+		if (!region->tree)
 			return Ok;
 
-		return GdipFillPath (graphics, brush, region->path);
+		/* (optimization) if there is only one path, then we do not need the bitmap */
+		if (region->tree->path) {
+			/* if the path is empty, return OK */
+			if (region->tree->path->count == 0)
+				return Ok;
+
+			/* else fill the single path */
+			return GdipFillPath (graphics, brush, region->tree->path);
+		}
+
+		gdip_region_bitmap_ensure (region);
+		if (!region->bitmap)
+			return OutOfMemory;
+
+		status = GdipCreateBitmapFromGraphics (region->bitmap->Width, region->bitmap->Height, graphics, &bitmap);
+		if (status == Ok) {
+			GpGraphics *bitgraph;
+			status = GdipGetImageGraphicsContext ((GpImage*)bitmap, &bitgraph);
+			if (status == Ok) {
+				/* fill the "full" rectangle using the specified brush */
+				GdipFillRectangle (bitgraph, brush, 0, 0, region->bitmap->Width, region->bitmap->Height);
+
+				/* adjust bitmap alpha (i.e. shape the brushed-rectangle like the region) */
+				gdip_region_bitmap_apply_alpha (bitmap, region->bitmap);
+
+				/* draw the region */
+				status = GdipDrawImageRect (graphics, (GpImage*)bitmap, region->bitmap->X, region->bitmap->Y,
+					region->bitmap->Width, region->bitmap->Height);
+
+				GdipDeleteGraphics (bitgraph);
+			}
+
+			GdipDisposeImage ((GpImage*)bitmap);
+		}
+		return status;
 	}
 
-	/* or multiple rectangles */
+	/* if there's no rectangles, we can return directly */
 	if (!region->rects || (region->cnt == 0))
 		return Ok;
 
@@ -3691,8 +3727,6 @@
 	best thing for now is keep track of what the user wants and let Cairo do its autoclipping
 */
 
-extern bool gdip_is_InfiniteRegion (GpRegion *region);
-
 void
 gdip_set_cairo_clipping (GpGraphics *graphics)
 {
@@ -3712,7 +3746,11 @@
 		}
 		break;
 	case RegionTypePath:
-		gdip_plot_path (graphics, graphics->clip->path, graphics->aa_offset_x, graphics->aa_offset_y);
+		/* FIXME - current clipping won't work on complex paths (e.g. after binary operations) */
+		if (graphics->clip->tree && graphics->clip->tree->path)
+			gdip_plot_path (graphics, graphics->clip->tree->path, graphics->aa_offset_x, graphics->aa_offset_y);
+		else
+			g_warning ("FIXME - gdip_set_cairo_clipping support for path region is incomplete");
 		break;
 	default:
 		g_warning ("Unknown region type %d", graphics->clip);
@@ -3784,8 +3822,13 @@
 	if (!graphics || !region)
 		return InvalidParameter;
 
-	GdipCombineRegionRegion (graphics->clip, region, combineMode);	
-	gdip_set_cairo_clipping (graphics);
+	if (gdip_is_InfiniteRegion (region)) {
+		GdipSetInfinite (graphics->clip);
+		cairo_reset_clip (graphics->ct);
+	} else {
+		GdipCombineRegionRegion (graphics->clip, region, combineMode);	
+		gdip_set_cairo_clipping (graphics);
+	}
 	return Ok;
 }
 

Added: trunk/libgdiplus/src/region-bitmap.c
===================================================================
--- trunk/libgdiplus/src/region-bitmap.c	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region-bitmap.c	2006-03-25 16:33:37 UTC (rev 58520)
@@ -0,0 +1,1224 @@
+/*
+ * Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ * Authors:
+ *	Sebastien Pouliot  <sebastien at ximian.com>
+ */
+
+#include "region.h"
+
+#if FALSE
+
+/*
+ * Debugging helpers
+ */
+
+static void
+display32 (unsigned char *shape, int width, int height)
+{
+	int i, j;
+
+	for (i = 0; i < height; i++) {
+		for (j = 0; j < width; j++) {
+			printf ("%s", (shape [(i*width + j) * 4] == 0) ? "." : "X");
+		}
+		printf ("\n");
+	}
+	printf ("\n");
+}
+
+void
+display (char* message, GpRegionBitmap *bitmap)
+{
+	int i = 0, j = 0, k;
+
+	printf ("\n%s\n\tbitmap X: %d, Y: %d, Width: %d, Height %d, Mask %p\n", message,
+		bitmap->X, bitmap->Y, bitmap->Width, bitmap->Height, bitmap->Mask);
+	if (!bitmap->Mask)
+		return;
+
+	while (i < SHAPE_SIZE(bitmap)) {
+		unsigned char b = bitmap->Mask [i++];
+		for (k = 0; k < 8; k++) {
+			if (j++ == bitmap->Width) {
+				j = 1;
+				printf ("\n");
+			}
+			printf ("%s", ((b & (1 << k)) == 0) ? "." : "X");
+		}
+	}
+	printf ("\n");
+}
+
+#endif
+
+
+/* Helpers */
+
+
+/*
+ * rect_union:
+ * @bitmap1: a GpRegionBitmap
+ * @bitmap2: a GpRegionBitmap
+ * @rect: a pointer to a GpRect
+ *
+ * Calculate a rectangle, @rect, that contains both @bitmap1 and @bitmap2
+ * rectangles.
+ */
+static void
+rect_union (GpRegionBitmap *bitmap1, GpRegionBitmap *bitmap2, GpRect *rect)
+{
+	int max_x_1 = bitmap1->X + bitmap1->Width;
+	int max_x_2 = bitmap2->X + bitmap2->Width;
+	int max_y_1 = bitmap1->Y + bitmap1->Height;
+	int max_y_2 = bitmap2->Y + bitmap2->Height;
+
+	rect->X = (bitmap1->X < bitmap2->X) ? bitmap1->X : bitmap2->X;
+	rect->Y = (bitmap1->Y < bitmap2->Y) ? bitmap1->Y : bitmap2->Y;
+	rect->Width = ((max_x_1 > max_x_2) ? max_x_1 : max_x_2) - rect->X;
+	rect->Height = ((max_y_1 > max_y_2) ? max_y_1 : max_y_2) - rect->Y;
+}
+
+
+/*
+ * rect_intersect:
+ * @bitmap1: a GpRegionBitmap
+ * @bitmap2: a GpRegionBitmap
+ * @rect: a pointer to a GpRect
+ *
+ * Calculate a rectangle, @rect, that represent the area shared by both 
+ * @bitmap1 and @bitmap2 rectangles.
+ */
+static void
+rect_intersect (GpRegionBitmap *bitmap1, GpRegionBitmap *bitmap2, GpRect *rect)
+{
+	rect->X = (bitmap1->X > bitmap2->X) ? bitmap1->X : bitmap2->X;
+	rect->Y = (bitmap1->Y > bitmap2->Y) ? bitmap1->Y : bitmap2->Y;
+	rect->Width = (((bitmap1->X + bitmap1->Width) < (bitmap2->X - bitmap2->Width)) ? 
+		(bitmap1->X + bitmap1->Width) : (bitmap2->X + bitmap2->Width)) - rect->X;
+	rect->Height = (((bitmap1->Y + bitmap1->Height) < (bitmap2->Y - bitmap2->Height)) ? 
+		(bitmap1->Y + bitmap1->Height) : (bitmap2->Y + bitmap2->Height)) - rect->Y;
+}
+
+
+/*
+ * rect_adjust_horizontal:
+ * @x: a pointer to an integer
+ * @width: a pointer to an integer
+ *
+ * Adjust the @x and @width values so that they both are multiples of eight
+ * and still encompass, at least, the same data as their original value.
+ */
+static void
+rect_adjust_horizontal (int *x, int *width)
+{
+	/* ensure that X is a multiple of 8 */
+	int i = (*x & 7);
+	if (i > 0) {
+		/* reduce X to be a multiple of 8*/
+		*x -= i;
+		/* but keep the "true" Width constant */
+		*width += i;
+	}
+	/* ensure that Width is a multiple of 8 */
+	i = (*width & 7);
+	if (i > 0) {
+		*width += (8 - i);
+	}
+}
+
+
+/*
+ * alloc_bitmap_memory:
+ * @size: the size of the required allocation
+ * @clear: a BOOL
+ *
+ * Allocate the alpha (1bpp) memory required for storing a bitmap and return
+ * a pointer to this memory. @clear decides if the memory will be zeroized
+ * after being allocated. NULL can be returned if too much memory is 
+ * requested (very large region) or if the memory couldn't be allocated (low
+ * memory).
+ */
+static unsigned char*
+alloc_bitmap_memory (int size, BOOL clear)
+{
+	unsigned char *buffer;
+
+	if ((size < 1) || (size > REGION_MAX_BITMAP_SIZE)) {
+		g_warning ("Requested %d bytes. Maximum size for region is %d bytes.", 
+			size, REGION_MAX_BITMAP_SIZE);
+		return NULL;
+	}
+
+	buffer = (unsigned char*) malloc (size);
+	if (clear)
+		memset (buffer, 0, size);
+
+	return buffer;
+}
+
+
+/*
+ * alloc_bitmap_with_buffer:
+ * @x: an integer representing the X coordinate of the bitmap
+ * @y: an integer representing the Y coordinate of the bitmap
+ * @width: an integer representing the Width of the bitmap
+ * @height: an integer representing the Height of the bitmap
+ * @buffer: a byte array of the bitmap data
+ *
+ * Allocate and return a new GpRegionBitmap structure using the supplied 
+ * @buffer.
+ *
+ * Notes:
+ * - The allocated structure must be freed using gdip_region_bitmap_free.
+ * - The bitmap @x and @width MUST BE multiple of 8.
+ * - The supplied @buffer MUST match the supplied width and height parameters.
+ */
+static GpRegionBitmap*
+alloc_bitmap_with_buffer (int x, int y, int width, int height, unsigned char *buffer)
+{
+	GpRegionBitmap *result = (GpRegionBitmap*) GdipAlloc (sizeof (GpRegionBitmap));
+
+	result->X = x;
+	result->Y = y;
+	result->Width = width;
+	result->Height = height;
+	result->Mask = buffer;
+	result->reduced = FALSE; /* bitmap size isn't optimal wrt contents */
+
+	return result;
+}
+
+
+/*
+ * alloc_bitmap:
+ * @x: an integer representing the X coordinate of the bitmap
+ * @y: an integer representing the Y coordinate of the bitmap
+ * @width: an integer representing the Width of the bitmap
+ * @height: an integer representing the Height of the bitmap
+ *
+ * Allocate and return a new GpRegionBitmap structure.
+ *
+ * Notes:
+ * - The allocated structure must be freed using gdip_region_bitmap_free.
+ * - The bitmap @x and @width will be adjusted to a multiple of 8.
+ */
+static GpRegionBitmap*
+alloc_bitmap (int x, int y, int width, int height)
+{
+	unsigned char *buffer;
+	int size;
+
+	/* ensure X and Width are multiple of 8 */
+	rect_adjust_horizontal (&x, &width);
+
+	size = (width * height >> 3); /* 1 bit per pixel */
+	buffer = alloc_bitmap_memory (size, TRUE);
+
+	return alloc_bitmap_with_buffer (x, y, width, height, buffer);
+}
+
+
+/*
+ * alloc_merged_bitmap:
+ * @bitmap1: a GpRegionBitmap
+ * @bitmap2: a GpRegionBitmap
+ *
+ * Allocate and return a new GpRegionBitmap that covers the total area 
+ * (single rectangle) of both @bitmap1 and @bitmap2.
+ *
+ * Notes:
+ * - The allocated structure must be freed using gdip_region_bitmap_free.
+ */
+static GpRegionBitmap*
+alloc_merged_bitmap (GpRegionBitmap *bitmap1, GpRegionBitmap *bitmap2)
+{
+	GpRect rect;
+
+	rect_union (bitmap1, bitmap2, &rect);
+	return alloc_bitmap (rect.X, rect.Y, rect.Width, rect.Height);
+}
+
+
+/*
+ * alloc_intersected_bitmap:
+ * @bitmap1: a GpRegionBitmap
+ * @bitmap2: a GpRegionBitmap
+ *
+ * Allocate and return a new GpRegionBitmap that covers only the shared 
+ * rectangle area of both @bitmap1 and @bitmap2.
+ *
+ * Notes:
+ * - The allocated structure must be freed using gdip_region_bitmap_free.
+ * - The bitmap width will be adjusted to a multiple of 8.
+ */
+static GpRegionBitmap*
+alloc_intersected_bitmap (GpRegionBitmap *bitmap1, GpRegionBitmap *bitmap2)
+{
+	GpRect rect;
+
+	rect_intersect (bitmap1, bitmap2, &rect);
+	return alloc_bitmap (rect.X, rect.Y, rect.Width, rect.Height);
+}
+
+
+/*
+ * gdip_region_bitmap_clone:
+ * @bitmap: a GpRegionBitmap
+ *
+ * Allocate and return new GpRegionBitmap containing a copy of @bitmap.
+ *
+ * Note: the allocated structure must be freed using gdip_region_bitmap_free.
+ */
+GpRegionBitmap*
+gdip_region_bitmap_clone (GpRegionBitmap *bitmap)
+{
+	unsigned char *buffer;
+	int size = (bitmap->Width * bitmap->Height >> 3); /* 1 bit per pixel */
+
+	buffer = alloc_bitmap_memory (size, FALSE);
+	if (buffer)
+		memcpy (buffer, bitmap->Mask, size);
+
+	return alloc_bitmap_with_buffer (bitmap->X, bitmap->Y, bitmap->Width, bitmap->Height, buffer);
+}
+
+
+/*
+ * empty_bitmap:
+ * @bitmap: a GpRegionBitmap
+ *
+ * Clear and, if required, free the mask of @bitmap. Note that the allocated 
+ * GpRegionBitmap structure MUST still be freed using gdip_region_bitmap_free.
+ */
+static void
+empty_bitmap (GpRegionBitmap *bitmap)
+{
+	bitmap->X = 0;
+	bitmap->Y = 0;
+	bitmap->Width = 0;
+	bitmap->Height = 0;
+
+	if (bitmap->Mask) {
+		free (bitmap->Mask);
+		bitmap->Mask = NULL;
+	}
+}
+
+
+/*
+ * gdip_region_bitmap_free:
+ * @bitmap: a GpRegionBitmap
+ *
+ * Free the region bitmap @bitmap.
+ */
+void
+gdip_region_bitmap_free (GpRegionBitmap *bitmap)
+{
+	empty_bitmap (bitmap);
+	GdipFree (bitmap);
+}
+
+
+/*
+ * reduce:
+ * @source: a byte array containing the 32bpp bitmap
+ * @width: the width of the bitmap
+ * @height: the height of the bitmap
+ * @dest: a byte array for the reduced (1bpp) bitmap
+ *
+ * Reduce a 32bpp bitmap @source into a 1bbp bitmap @dest.
+ */
+static void
+reduce (unsigned char* source, int width, int height, unsigned char *dest)
+{
+	int i, j, n = 0, value = 0;
+
+	for (i = 0; i < height; i++) {
+		for (j = 0; j < width; j++) {
+			int pos = (i * width + j) * 4;
+			unsigned char combine = source [pos++] | source [pos++] | source [pos++] | source [pos];
+
+			if (combine != 0)
+				value |= 128;
+
+			if (++n == 8) {
+				*dest++ = value;
+				n = 0;
+				value = 0;
+			} else {
+				value >>= 1;
+			}
+		}
+	}
+}
+
+
+/*
+ * gdip_region_bitmap_apply_alpha:
+ * @bitmap: a GpBitmap (not a GpRegionBitmap!)
+ * @alpha: a GpRegionBitmap
+ *
+ * Apply the alpha bits (from @alpha) to a ARGB32 (or RGB24) @bitmap.
+ */
+void 
+gdip_region_bitmap_apply_alpha (GpBitmap *bitmap, GpRegionBitmap *alpha)
+{
+	int x, y, p = 0, n = 3; /* FIXME - is it endian safe ? */
+
+	for (y = 0; y < alpha->Height; y++) {
+		for (x = 0; x < alpha->Width; x += 8) {
+			// ARGB32
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x01) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x02) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x04) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x08) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x10) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x20) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x40) ? 0xFF : 0x00;
+			n += 4;
+			bitmap->data.Scan0 [n] = (alpha->Mask [p] & 0x80) ? 0xFF : 0x00;
+			n += 4;
+
+			p++;
+		}
+	}
+}
+
+
+/*
+ * gdip_region_bitmap_from_tree:
+ * @tree: a GpPathTree
+ *
+ * Return a new GpRegionBitmap containing the bitmap recomposed from the 
+ * @tree.
+ *
+ * Note: the allocated structure must be freed using gdip_region_bitmap_free.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_from_tree (GpPathTree *tree)
+{
+	GpRegionBitmap *result;
+
+	if (!tree)
+		return NULL;
+
+	/* each item has... */
+	if (tree->path) {
+		/* (a) only a path (the most common case) */
+		result = gdip_region_bitmap_from_path (tree->path);
+	} else {
+		/* (b) two items with an binary operation */
+		GpRegionBitmap *bitmap1 = gdip_region_bitmap_from_tree (tree->branch1);
+		GpRegionBitmap *bitmap2 = gdip_region_bitmap_from_tree (tree->branch2);
+
+		result = gdip_region_bitmap_combine (bitmap1, bitmap2, tree->mode);
+
+		if (bitmap1)
+			gdip_region_bitmap_free (bitmap1);
+		if (bitmap2)
+			gdip_region_bitmap_free (bitmap2);
+	}
+	return result;
+}
+
+
+/*
+ * gdip_region_bitmap_ensure:
+ * @region: a GpRegion
+ *
+ * Ensure the @region bitmap is available (as it isn't created until it is 
+ * actually needed).
+ */
+void
+gdip_region_bitmap_ensure (GpRegion *region)
+{
+	/* we already have the bitmap */
+	if (region->bitmap)
+		return;
+
+	/* redraw the bitmap from the original path + all other operations/paths */
+	region->bitmap = gdip_region_bitmap_from_tree (region->tree);
+}
+
+
+/*
+ * gdip_region_bitmap_invalidate:
+ * @region: a GpRegion
+ *
+ * Invalidate (and free) the bitmap (if any) associated with @region. The 
+ * bitmap will need to be re-created before begin used.
+ */
+void
+gdip_region_bitmap_invalidate (GpRegion *region)
+{
+	/* it's possible that the bitmap hasn't yet been created (e.g. if
+	   a rectangle region has just been converted to a path region) */
+	if (!region->bitmap)
+		return;
+
+	empty_bitmap (region->bitmap);
+	region->bitmap = NULL;
+}
+
+
+/*
+ * gdip_region_bitmap_from_path:
+ * @path: a GpPath
+ *
+ * Return a new GpRegionBitmap containing the bitmap representing the @path.
+ * NULL will be returned if the bitmap cannot be created (e.g. too big).
+ *
+ * Note: the allocated structure must be freed using gdip_region_bitmap_free.
+ */
+GpRegionBitmap*
+gdip_region_bitmap_from_path (GpPath *path)
+{
+	GpRect bounds;
+	GpRegionBitmap *bitmap;
+	unsigned char* buffer;
+        int i, idx, stride;
+	int length = path->count;
+	unsigned long size;
+
+	/* empty path == empty bitmap */
+	if (length == 0)
+		return alloc_bitmap_with_buffer (0, 0, 0, 0, NULL);
+
+	/* get the limits of the bitmap we need to allocate */
+	if (GdipGetPathWorldBoundsI (path, &bounds, NULL, NULL) != Ok)
+		return NULL;
+
+	/* ensure X and Width are multiple of 8 */
+	rect_adjust_horizontal (&bounds.X, &bounds.Width);
+
+	/* replay the path list and the operations to reconstruct the bitmap */
+	stride = bounds.Width * 4;	/* RGBA -> 32 bpp, 4 Bbp */
+	size = stride * bounds.Height;
+
+	/* here the memory is allocated for a ARGB bitmap - so 32 times bigger than our normal alpha bitmap */
+	if ((size < 1) || (size > REGION_MAX_BITMAP_SIZE << 5)) {
+		g_warning ("Path convertion requested %d bytes (%d x %d). Maximum size is %d bytes.",
+			size, bounds.Width, bounds.Height, REGION_MAX_BITMAP_SIZE << 5);
+		return NULL;
+	}
+	buffer = (unsigned char*) malloc (size);
+	if (!buffer)
+		return NULL;
+	memset (buffer, 0, size);
+
+	cairo_surface_t *surface = cairo_image_surface_create_for_data (buffer, 
+		CAIRO_FORMAT_ARGB32, bounds.Width, bounds.Height, stride);
+	cairo_t *cr = cairo_create (surface);
+
+	idx = 0;
+	for (i = 0; i < length; ++i) {
+		GpPointF pt = g_array_index (path->points, GpPointF, i);
+		byte type = g_array_index (path->types, byte, i);
+		GpPointF pts [3];
+		/* mask the bits so that we get only the type value not the other flags */
+		switch (type & PathPointTypePathTypeMask) {
+		case PathPointTypeStart:
+			cairo_move_to (cr, pt.X - bounds.X, pt.Y - bounds.Y);
+			break;
+		case PathPointTypeLine:
+			cairo_line_to (cr, pt.X - bounds.X, pt.Y - bounds.Y);
+			break;
+		case PathPointTypeBezier:
+			/* make sure we only add at most 3 points to pts */
+			if (idx < 3) {
+				pts [idx] = pt;
+				idx ++;
+			}
+			/* once we've added 3 pts, we can draw the curve */
+			if (idx == 3) {
+				cairo_curve_to (cr, pts [0].X - bounds.X, pts [0].Y - bounds.Y, 
+					pts [1].X - bounds.X, pts [1].Y - bounds.Y, 
+					pts [2].X - bounds.X, pts [2].Y - bounds.Y);
+				idx = 0;
+			}
+                        break;
+                }
+
+		/* close the subpath */
+		if (type & PathPointTypeCloseSubpath)
+			cairo_close_path (cr);
+        }
+
+	cairo_clip (cr);
+	cairo_set_source_rgba (cr, 1, 1, 1, 1);
+	cairo_paint (cr);
+	cairo_destroy (cr);
+
+	cairo_surface_destroy (surface);
+
+	bitmap = alloc_bitmap (bounds.X, bounds.Y, bounds.Width, bounds.Height);
+	reduce (buffer, bounds.Width, bounds.Height, bitmap->Mask);
+	free (buffer);
+
+	return bitmap;
+}
+
+
+/*
+ * gdip_region_bitmap_get_smallest_rect:
+ * @bitmap: a GpRegionBitmap
+ * @rect: a pointer to a GpRect
+ *
+ * Return the minimal used space in the bitmap inside @rect.
+ */
+void
+gdip_region_bitmap_get_smallest_rect (GpRegionBitmap *bitmap, GpRect *rect)
+{
+	int first_y = bitmap->Height + 1;	/* empty (top) lines */
+	int last_y = -1;			/* empty (bottom) lines */
+	int first_x = bitmap->Width + 1;	/* empty (left) columns */
+	int last_x = -1;			/* empty (right) columns */
+	int i = 0;
+	int original_size = SHAPE_SIZE(bitmap);
+	int old_width_byte = bitmap->Width >> 3;
+	int x = 0, y = 0;
+
+	while (i < original_size) {
+		if (bitmap->Mask [i++] != 0) {
+			if (x < first_x)
+				first_x = x;
+			if (x > last_x)
+				last_x = x;
+			if (y < first_y)
+				first_y = y;
+			if (y > last_y)
+				last_y = y;
+		}
+		if (++x == old_width_byte) {
+			x = 0;
+			y++;
+		}
+	}
+
+	/* did we found some bits ? */
+	if ((last_x == -1) && (last_y == -1) && (first_x == bitmap->Width + 1) && (first_y == bitmap->Height + 1)) {
+		rect->X = rect->Y = rect->Width = rect->Height = 0;
+	} else {
+		// convert to pixel values
+		rect->X = bitmap->X + (first_x << 3);
+		rect->Y = bitmap->Y + first_y;
+		rect->Width = abs (((last_x + 1) << 3) - first_x);
+		rect->Height = last_y - first_y + 1;
+	}
+}
+
+
+/*
+ * is_worth_shrinking:
+ * @original_size: the original size of a bitmap
+ * @new_size: the _potential_ new size of the bitmap (if shrinked)
+ *
+ * Decide if the current bitmap, based on it's current size, is worth 
+ * shrinking to a lesser size.
+ *
+ * Note: Many binary operations (e.g. intersection) can greatly reduce the
+ * size of the final bitmap.
+ */
+static BOOL
+is_worth_shrinking (int original_size, int new_size)
+{
+	/* FIXME - we can do better than checking if we "save" 4kb */
+	return ((original_size - new_size) > 4096);
+}
+
+
+/*
+ * gdip_region_bitmap_shrink:
+ * @bitmap: a GpRegionBitmap
+ * @always_shrink: a BOOL
+ *
+ * Shrink the @bitmap if either @always_shrink is TRUE, or if it is decided
+ * to be worth the CPU time (see is_worth_shrinking).
+ *
+ * Reducing the bitmap size permit (a) to reduce the memory footprint and
+ * (b) makes it more likely to apply certain optimizations using rectangle
+ * intersections.
+ *
+ * Notes: 
+ * 1.	we don't call this after an union (because the result will never be
+ *	smaller) but other operations can result in a smaller bitmap.
+ * 2.	we keep the bitmap width in multiple of 8 - it's simpler and faster
+ */
+void
+gdip_region_bitmap_shrink (GpRegionBitmap *bitmap, BOOL always_shrink)
+{
+	int original_size, new_size;
+	BOOL can_be_reduced;
+	GpRect rect;
+	int i;
+
+	/* bitmap (a) was already shrinked, or (b) is empty */
+	if (bitmap->reduced || !bitmap->Mask)
+		return;
+
+	gdip_region_bitmap_get_smallest_rect (bitmap, &rect);
+
+	if ((rect.Width == 0) || (rect.Height == 0)) {
+		/* no, the the bitmap is empty */
+		empty_bitmap (bitmap);
+		return;
+	}
+
+	/* ensure X and Width are multiple of 8 */
+	rect_adjust_horizontal (&rect.X, &rect.Width);
+
+	original_size = SHAPE_SIZE(bitmap);
+	new_size = (rect.Height * rect.Width) >> 3; /* bits->bytes */
+	can_be_reduced = (new_size < original_size);
+
+	/* shrink if:
+	 * a. the caller asked for it (and there is a size change)
+	 * b. the caller didn't ask for it but "we" decided it's worth it
+	 */
+	if ((always_shrink && can_be_reduced) || is_worth_shrinking (original_size, new_size)) {
+		/* reallocate a new bitmap buffer */
+		unsigned char *new_mask = alloc_bitmap_memory (new_size, FALSE);
+		if (!new_mask)
+			return;
+
+		int new_width = rect.Width;
+		int new_height = rect.Height;
+		int x, y;
+
+		int old_width_byte = bitmap->Width >> 3;
+		int new_width_byte = new_width >> 3;
+
+		unsigned char* newline = new_mask;
+		unsigned char* oldline = bitmap->Mask + ((rect.Y - bitmap->Y) * old_width_byte) + ((rect.X - bitmap->X) >> 3);
+		/* copy the interesting portion in the new bitmap */
+		for (y = 0; y < new_height; y++) {
+			memcpy (newline, oldline, new_width_byte);
+			newline += new_width_byte;
+			oldline += old_width_byte;
+		}
+
+		/* replace current data */
+		bitmap->X = rect.X;
+		bitmap->Y = rect.Y;
+		bitmap->Width = rect.Width;
+		bitmap->Height = rect.Height;
+		free (bitmap->Mask);
+		bitmap->Mask = new_mask;
+		bitmap->reduced = TRUE;
+	}
+}
+
+
+/*
+ * is_point_visible:
+ * @bitmap: a GpRegionBitmap
+ * @x: the horizontal position
+ * @y: the vertical position
+ *
+ * Return TRUE if the @x, at y point is set on the bitmap.
+ *
+ * Note: No bounds check are done this internal shared function.
+ */
+static BOOL
+is_point_visible (GpRegionBitmap *bitmap, int x, int y)
+{
+	int pixel, pos, mask;
+
+	/* is the pixel set ? */
+	x -= bitmap->X;
+	y -= bitmap->Y;
+
+	pixel = (y * bitmap->Width + x);
+	pos = (pixel >> 3);
+	mask = (pixel & 7);
+
+	return ((bitmap->Mask [pos] & (1 << mask)) != 0);
+}
+
+
+/*
+ * gdip_region_bitmap_is_point_visible:
+ * @bitmap: a GpRegionBitmap
+ * @x: the horizontal position
+ * @y: the vertical position
+ *
+ * Return TRUE if the @x, at y point is set on the bitmap.
+ *
+ * Note: Using a bitmap reduce the precision to integers.
+ */
+BOOL
+gdip_region_bitmap_is_point_visible (GpRegionBitmap *bitmap, int x, int y)
+{
+	/* is this an empty bitmap ? */
+	if ((bitmap->Width == 0) || (bitmap->Height == 0))
+		return FALSE;
+
+	/* is the point inside the bitmap ? */
+	if ((x < bitmap->X) || (x >= bitmap->X + bitmap->Width))
+		return FALSE;
+	if ((y < bitmap->Y) || (y >= bitmap->Y + bitmap->Height))
+		return FALSE;
+
+	return is_point_visible (bitmap, x, y);
+}
+
+
+/*
+ * gdip_region_bitmap_is_point_visible:
+ * @bitmap: a GpRegionBitmap
+ * @rect: a pointer to a GpRect
+ *
+ * Return TRUE is _any_ part of @rect is inside the region.
+ */
+BOOL
+gdip_region_bitmap_is_rect_visible (GpRegionBitmap *bitmap, GpRect *rect)
+{
+	int x, y;
+
+	/* is this an empty bitmap ? */
+	if ((bitmap->Width == 0) || (bitmap->Height == 0))
+		return FALSE;
+
+	/* quick intersection checks */
+	if (bitmap->X < rect->X + rect->Width)
+		return FALSE;
+	if (bitmap->X + bitmap->Width > rect->X)
+		return FALSE;
+	if (bitmap->Y < rect->Y + rect->Height)
+		return FALSE;
+	if (bitmap->Y + bitmap->Height > rect->Y)
+		return FALSE;
+
+	/* TODO - optimize */
+	for (y = rect->Y; y < rect->Y + rect->Height; y++) {
+		for (x = rect->X; x < rect->X + rect->Width; x++) {
+			if (is_point_visible (bitmap, x, y))
+				return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+
+/*
+ * get_buffer_pos:
+ * @shape: a GpRegionBitmap
+ * @x: the horizontal position
+ * @y: the vertical position
+ *
+ * Return the index, inside the @shape buffer, corresponding to the @x, at y 
+ * point.
+ */
+static int
+get_buffer_pos (GpRegionBitmap *shape, int x, int y)
+{
+	/* check for out of bounds */
+	if ((x < shape->X) || (x >= shape->X + shape->Width))
+		return -1;
+	if ((y < shape->Y) || (y >= shape->Y + shape->Height))
+		return -1;
+
+	x -= shape->X;
+	y -= shape->Y;
+	return ((y * shape->Width + x) >> 3);
+}
+
+
+/*
+ * get_byte:
+ * @shape: a GpRegionBitmap
+ * @x: the horizontal position
+ * @y: the vertical position
+ *
+ * Return the byte, from the @shape buffer, corresponding to the @x, at y point.
+ * Note that this byte contains 8 pixels.
+ */
+static int
+get_byte (GpRegionBitmap *shape, int x, int y)
+{
+	/* out of bounds == empty (no pixel) */
+	int pos = get_buffer_pos (shape, x, y);
+	return (pos == -1) ? 0 : shape->Mask [pos];
+}
+
+
+/*
+ * Process a single line for gdip_region_bitmap_get_scans.
+ */
+static BOOL
+process_line (GpRegionBitmap *bitmap, int y, int *x, int *w)
+{
+	int pos = *x;
+	*x = -1;
+	*w = -1;
+
+	while (pos < bitmap->X + bitmap->Width) {
+		BOOL visible = gdip_region_bitmap_is_point_visible (bitmap, pos, y);
+		if (*x == -1) {
+			if (visible) {
+				*x += pos + 1;
+			}
+		} else {
+			if (!visible) {
+				*w = pos - *x;
+				return TRUE;
+			}
+		}
+		pos++;
+	}
+
+	/* end of line - have we started a rect ? */
+	if (*x != -1) {
+		*w = pos - *x;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/*
+ * gdip_region_bitmap_get_scans:
+ * @bitmap: a GpRegionBitmap
+ * @rect: a pointer to an array of GpRectF
+ * @count: the number of GpRectF in the array
+ *
+ * Convert the scan lines of the bitmap into an array of GpRectF. The return
+ * value represents the actual number of GpRectF entries that were generated.
+ */
+int
+gdip_region_bitmap_get_scans (GpRegionBitmap *bitmap, GpRectF *rect, int count)
+{
+	GpRect actual;
+	int x, y, w;
+	int n = 0;
+	
+	actual.X = REGION_INFINITE_POSITION;
+	actual.Width = REGION_INFINITE_LENGTH;
+	/* for each line in the bitmap */
+	for (y = bitmap->Y; y < bitmap->Y + bitmap->Height; y++) {
+		/* until we processed the whole line */
+		x = bitmap->X;
+		while (process_line (bitmap, y, &x, &w)) {
+			/* FIXME - we only look at the last rectangle but we could check all
+			 	rectangles in the previous line (and retain perfect rendering
+				with, possibly, less rectangle. We could also allow non exact
+				match for X and Width (e.g. +/- 1 pixel). MS doesn't seems to
+				return perfect rectangles for all shapes. */
+
+			/* if position (X) and Width are identical to previous rectangle */
+			if ((x == actual.X) && (w == actual.Width)) {
+				/* then augment it's Height by one */
+				if (rect && (n > 0)) {
+					rect [n - 1].Height++;
+				}
+			} else {
+				actual.X = x;
+				actual.Y = y;
+				actual.Width = w;
+				actual.Height = 1;
+
+				if (rect && (n < count)) {
+					rect [n].X = actual.X;
+					rect [n].Y = actual.Y;
+					rect [n].Width = actual.Width;
+					rect [n].Height = actual.Height;
+				}
+				n++;
+			}
+			/* continue on the same line */
+			x += w + 1;
+		}
+	}
+	return n;
+}
+
+
+/*
+ * Binary operators helper functions
+ */
+
+
+/* 
+ * bitmap_intersect:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * This function checks if the rectangle containing @shape1 intersect with
+ * the rectangle containing @shape2. It is used to optimize certain code
+ * path in the binary operations.
+ */
+static BOOL
+bitmap_intersect (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	return ((shape1->X < shape2->X + shape2->Width) &&
+		(shape1->X + shape1->Width > shape2->X) &&
+		(shape1->Y < shape2->Y + shape2->Height) &&
+		(shape1->Y + shape1->Height > shape2->Y));
+}
+
+
+/* 
+ * gdip_region_bitmap_compare:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * This function checks if the data inside @shape1 is identical to the data
+ * inside @shape2 - even if their respective rectangles are different.
+ */
+BOOL
+gdip_region_bitmap_compare (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRect rect;
+	int x, y;
+
+	/* if the rectangles containing shape1 and shape2 DO NOT
+	   intersect, then there is no possible intersection */
+	if (!bitmap_intersect (shape1, shape2))
+		return FALSE;
+
+	rect_union (shape1, shape2, &rect);
+	for (y = rect.Y; y < rect.Y + rect.Height; y++) {
+		for (x = rect.X; x < rect.X + rect.Width; x += 8) {
+			if (get_byte (shape1, x, y) != get_byte (shape2, x, y))
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+
+/*
+ * Binary operators on bitmap regions
+ *
+ * Notes
+ * - All operations requires the bitmap x origin and it's width to be multiple
+ *   of 8.
+ */
+
+
+/*
+ * gdip_region_bitmap_union:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * Return a new bitmap containing the union of the two specified region 
+ * bitmaps.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_union (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRegionBitmap *op = alloc_merged_bitmap (shape1, shape2);
+	int x, y;
+
+	for (y = op->Y; y < op->Y + op->Height; y++) {
+		int p = get_buffer_pos (op, op->X, y);
+		for (x = op->X; x < op->X + op->Width; x += 8) {
+			op->Mask [p++] = get_byte (shape1, x, y) | get_byte (shape2, x, y);
+		}
+	}
+
+	/* no need to call reduce_bitmap (it will never shrink, 
+	   unless the original bitmap were oversized) */
+	return op;
+}
+
+
+/*
+ * gdip_region_bitmap_intersection:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * Return a new bitmap containing the intersection of the two specified region
+ * bitmaps.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_intersection (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRegionBitmap *op;
+	int x, y;
+
+	/* if the rectangles containing shape1 and shape2 DO NOT
+	   intersect, then there is no possible intersection */
+	if (!bitmap_intersect (shape1, shape2))
+		return alloc_bitmap_with_buffer (0, 0, 0, 0, NULL);
+
+	/* the bitmap size cannot be bigger than a rectangle intersection of
+	   both bitmaps */
+	op = alloc_intersected_bitmap (shape1, shape2);
+
+	for (y = op->Y; y < op->Y + op->Height; y++) {
+		int p = get_buffer_pos (op, op->X, y);
+		for (x = op->X; x < op->X + op->Width; x += 8) {
+			op->Mask [p++] = get_byte (shape1, x, y) & get_byte (shape2, x, y);
+		}
+	}
+
+	/* reduce bitmap size - if it make sense */
+	gdip_region_bitmap_shrink (op, FALSE);
+	return op;
+}
+
+
+/*
+ * gdip_region_bitmap_exclude:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * Return a new bitmap containing the first shape minus the second shape.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_exclude (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRegionBitmap *op;
+	int x, y;
+
+	/* if the rectangles containing shape1 and shape2 DO NOT
+	   intersect, then the result is identical shape1 */
+	if (!bitmap_intersect (shape1, shape2))
+		return gdip_region_bitmap_clone (shape1);
+
+	/* the new bitmap size cannot be bigger than shape1 */
+	op = alloc_bitmap (shape1->X, shape1->Y, shape1->Width, shape1->Height);
+
+	for (y = op->Y; y < op->Y + op->Height; y++) {
+		int p = get_buffer_pos (op, op->X, y);
+		for (x = op->X; x < op->X + op->Width; x += 8) {
+			unsigned char b1 = get_byte (shape1, x, y);
+			op->Mask [p++] = b1 - (b1 & get_byte (shape2, x, y));
+		}
+	}
+
+	/* reduce bitmap size - if it make sense */
+	gdip_region_bitmap_shrink (op, FALSE);
+	return op;
+}
+
+
+/*
+ * gdip_region_bitmap_complement:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * Return a new bitmap containing the second shape minus the first shape.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_complement (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRegionBitmap *op;
+	int x, y;
+
+	/* if the rectangles containing shape1 and shape2 DO NOT
+	   intersect, then the result is identical shape2 */
+	if (!bitmap_intersect (shape1, shape2))
+		return gdip_region_bitmap_clone (shape2);
+
+	/* the new bitmap size cannot be bigger than shape2 */
+	op = alloc_bitmap (shape2->X, shape2->Y, shape2->Width, shape2->Height);
+
+	for (y = op->Y; y < op->Y + op->Height; y++) {
+		int p = get_buffer_pos (op, op->X, y);
+		for (x = op->X; x < op->X + op->Width; x += 8) {
+			unsigned char b2 = get_byte (shape2, x, y);
+			op->Mask [p++] = b2 - (b2 & get_byte (shape1, x, y));
+		}
+	}
+
+	/* reduce bitmap size - if it make sense */
+	gdip_region_bitmap_shrink (op, FALSE);
+	return op;
+}
+
+
+/*
+ * gdip_region_bitmap_xor:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ *
+ * Return a new bitmap containing the exclusive-or of the two specified region
+ * bitmaps.
+ */
+static GpRegionBitmap*
+gdip_region_bitmap_xor (GpRegionBitmap *shape1, GpRegionBitmap *shape2)
+{
+	GpRegionBitmap *op;
+	int x, y;
+
+	/* if the rectangles containing shape1 and shape2 DO NOT intersect,
+	   then the result is identical an union of shape1 and shape2. Code is
+	   almost similar but no reduction is required for an union. */
+	if (!bitmap_intersect (shape1, shape2))
+		return gdip_region_bitmap_union (shape1, shape2);
+
+	/* the new bitmap is potentially as big as the two merged bitmaps */
+	op = alloc_merged_bitmap (shape1, shape2);
+
+	for (y = op->Y; y < op->Y + op->Height; y++) {
+		int p = get_buffer_pos (op, op->X, y);
+		for (x = op->X; x < op->X + op->Width; x += 8) {
+			op->Mask [p++] = get_byte (shape1, x, y) ^ get_byte (shape2, x, y);
+		}
+	}
+
+	/* reduce bitmap size - if it make sense */
+	gdip_region_bitmap_shrink (op, FALSE);
+	return op;
+}
+
+
+/*
+ * gdip_region_bitmap_combine:
+ * @shape1: a GpRegionBitmap
+ * @shape2: a GpRegionBitmap
+ * @combineMode: the binary operator to apply between the two shapes
+ *
+ * Return a new GpRegionBitmap containing a new bitmap resulting from applying
+ * the @combineMode to @shape1 and @shape2 bitmaps.
+ */
+GpRegionBitmap*
+gdip_region_bitmap_combine (GpRegionBitmap *bitmap1, GpRegionBitmap* bitmap2, CombineMode combineMode)
+{
+	GpRegionBitmap *result = NULL;
+
+	if (!bitmap1 || !bitmap2)
+		return NULL;
+
+	switch (combineMode) {
+	case CombineModeComplement:
+		return gdip_region_bitmap_complement (bitmap1, bitmap2);
+	case CombineModeExclude:
+		return gdip_region_bitmap_exclude (bitmap1, bitmap2);
+	case CombineModeIntersect:
+		return gdip_region_bitmap_intersection (bitmap1, bitmap2);
+	case CombineModeUnion:
+		return gdip_region_bitmap_union (bitmap1, bitmap2);
+	case CombineModeXor:
+		return gdip_region_bitmap_xor (bitmap1, bitmap2);
+	default:
+		g_warning ("Unkown combine mode specified (%d)", combineMode);
+		return NULL;
+	}
+}

Added: trunk/libgdiplus/src/region-bitmap.h
===================================================================
--- trunk/libgdiplus/src/region-bitmap.h	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region-bitmap.h	2006-03-25 16:33:37 UTC (rev 58520)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ * Authors:
+ *          Sebastien Pouliot  <sebastien at ximian.com>
+ */
+
+#ifndef _REGION_BITMAP_H_
+#define _REGION_BITMAP_H_
+
+#include "gdip.h"
+
+/* internal (private) API for regions bitmaps */
+
+/*
+ * REGION_MAX_BITMAP_SIZE defines the size limit of the region bitmap we keep
+ * in memory. The current value is 2 megabits which should be enough for any 
+ * on-screen region. Before changing this value remember that a "real", but 
+ * temporary, ARGB32 bitmap (32 times bigger, i.e. 8MB) may be allocated when 
+ * converting the path into the region bitmap.
+ */
+#define REGION_MAX_BITMAP_SIZE		(2 * 1024 * 1024 >> 3)
+
+#define SHAPE_SIZE(shape)		(((shape)->Width * (shape)->Height) >> 3)
+
+/* bitmap creation */
+void gdip_region_bitmap_ensure (GpRegion *region);
+GpRegionBitmap* gdip_region_bitmap_from_path (GpPath *path);
+GpRegionBitmap* gdip_region_bitmap_clone (GpRegionBitmap *bitmap);
+
+void gdip_region_bitmap_free (GpRegionBitmap *bitmap);
+void gdip_region_bitmap_invalidate (GpRegion *region);
+
+BOOL gdip_region_bitmap_compare (GpRegionBitmap *shape1, GpRegionBitmap *shape2);
+BOOL gdip_region_bitmap_is_point_visible (GpRegionBitmap *bitmap, int x, int y);
+BOOL gdip_region_bitmap_is_rect_visible (GpRegionBitmap *bitmap, GpRect *rect);
+
+int gdip_region_bitmap_get_scans (GpRegionBitmap *bitmap, GpRectF *rect, int count);
+
+void gdip_region_bitmap_get_smallest_rect (GpRegionBitmap *bitmap, GpRect *rect);
+void gdip_region_bitmap_shrink (GpRegionBitmap *bitmap, BOOL always_shrink);
+
+void gdip_region_bitmap_apply_alpha (GpBitmap *bitmap, GpRegionBitmap *alpha);
+
+GpRegionBitmap* gdip_region_bitmap_combine (GpRegionBitmap *bitmap1, GpRegionBitmap* bitmap2, CombineMode combineMode);
+
+#endif /* _REGION_BITMAP_H_ */

Added: trunk/libgdiplus/src/region-path-tree.c
===================================================================
--- trunk/libgdiplus/src/region-path-tree.c	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region-path-tree.c	2006-03-25 16:33:37 UTC (rev 58520)
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ * Authors:
+ *          Sebastien Pouliot  <sebastien at ximian.com>
+ */
+
+#include "region.h"
+
+/*
+ * gdip_region_clear_tree:
+ * @tree: a GpPathTree to clear
+ *
+ * Recursively clear (delete path and free memory) the specified path tree.
+ */
+void 
+gdip_region_clear_tree (GpPathTree *tree)
+{
+	if (!tree)
+		return;
+
+	if (tree->path) {
+		GdipDeletePath (tree->path);
+		tree->path = NULL;
+	} else {
+		gdip_region_clear_tree (tree->branch1);
+                GdipFree (tree->branch1);
+		gdip_region_clear_tree (tree->branch2);
+                GdipFree (tree->branch2);
+	}
+}
+
+
+/*
+ * gdip_region_copy_tree:
+ * @source: the GpPathTree to copy
+ * @dest: the GpPathTree copy
+ *
+ * Recursively copy (and allocate) the @source path tree into @dest.
+ * If @source is present then we must have a valid @dest.
+ */
+void 
+gdip_region_copy_tree (GpPathTree *source, GpPathTree *dest)
+{
+	if (!source)
+		return;
+
+	g_assert (dest);
+	if (source->path) {
+		GdipClonePath (source->path, &dest->path);
+		dest->branch1 = NULL;
+		dest->branch2 = NULL;
+	} else {
+		dest->path = NULL;
+		dest->mode = source->mode;
+		dest->branch1 = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+		gdip_region_copy_tree (source->branch1, dest->branch1);
+		dest->branch2 = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+		gdip_region_copy_tree (source->branch2, dest->branch2);
+	}
+}
+
+
+/*
+ * gdip_region_get_tree_size:
+ * @tree: a GpPathTree
+ *
+ * Recursively calculate the size (in bytes) required to serialized @tree.
+ */
+UINT
+gdip_region_get_tree_size (GpPathTree *tree)
+{
+	UINT result;
+
+	if (tree->path) {
+		/* tag, count, fillmode, types and points */
+		result = 3 * sizeof (guint32) + 
+			(tree->path->count * sizeof (guint8)) +
+			(tree->path->count * sizeof (GpPointF));
+	} else {
+		/* tag, operation, size (branch1), branch1, size (branch2), branch2 */
+		result = 4 * sizeof (guint32);
+		result += gdip_region_get_tree_size (tree->branch1);
+		result += gdip_region_get_tree_size (tree->branch2);
+	}
+	return result;
+}
+
+
+/*
+ * gdip_region_deserialize_tree:
+ * @data: a byte array
+ * @size: the length of the byte array
+ * @tree: a GpPathTree
+ *
+ * Recursively deserialize the @tree from the supplied buffer @data. Returns 
+ * TRUE if the deserialization was possible, or FALSE if a problem was found
+ * (e.g. @size mismatch, bad data...)
+ */
+BOOL
+gdip_region_deserialize_tree (BYTE *data, int size, GpPathTree *tree)
+{
+	int len = sizeof (guint32);
+	guint32 tag = (guint32) *data;
+	data += len;
+	size -= len;
+
+	switch (tag) {
+	case REGION_TAG_PATH: {
+		/* deserialize a path from the memory blob */
+		guint32 count;
+		GpFillMode mode;
+
+		tree->mode = CombineModeReplace;
+		tree->branch1 = NULL;
+		tree->branch2 = NULL;
+		/* count */
+		count = (guint32) *data;
+		data += len;
+		size -= len;
+		/* mode (FillMode, not CombineMode) */
+		mode = (GpFillMode) *data;
+		data += len;
+		size -= len;
+		/* check that the size match the length of the type (byte) and 
+		   GpPointF for the specified count */
+		if (size == count + count * sizeof (GpPointF)) {
+			BYTE* types = data;
+			GpPointF *points = (GpPointF*) (data + count);
+			return (GdipCreatePath2 (points, types, count, mode, &tree->path) == Ok);
+		}
+		return FALSE;
+		}
+		break;
+	case REGION_TAG_TREE: {
+		guint branch_size;
+		tree->path = NULL;
+		/* operation */
+		tree->mode = (CombineMode) *data;
+		data += len;
+		size -= len;
+		/* size (branch1) */
+		branch_size = (guint32) *data;
+		data += len;
+		size -= len;
+		/* deserialize a tree from the memory blob */
+		tree->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+		if (!gdip_region_deserialize_tree (data, branch_size, tree->branch1))
+			return FALSE;
+		data += branch_size;
+		size -= branch_size;
+		/* size (branch2) */
+		branch_size = (guint32) *data;
+		data += len;
+		size -= len;
+		tree->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+		if (!gdip_region_deserialize_tree (data, branch_size, tree->branch2))
+			return FALSE;
+		}
+		break;
+	default:
+		g_warning ("Invalid tag %d", tag);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/*
+ * gdip_region_serialize_tree:
+ * @tree: a GpPathTree
+ * @buffer: a byte array
+ * @bufferSize: the length of the byte array
+ * @sizeFilled: a pointer to a integer
+ *
+ * Recursively serialize the @tree data in the supplied @buffer. Returns TRUE
+ * if the serialization was possible, or FALSE if a problem was found (e.g. 
+ * @bufferSize too small). If successful @sizeFilled will contains the actual
+ * number of bytes that were required to serialize @tree.
+ */
+BOOL
+gdip_region_serialize_tree (GpPathTree *tree, BYTE *buffer, UINT bufferSize, UINT *sizeFilled)
+{
+	if (tree->path) {
+		/* tag */
+		guint32 temp = REGION_TAG_PATH;
+		int len = sizeof (guint32);
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* count */
+		memcpy (buffer, &tree->path->count, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* fill_mode */
+		temp = tree->path->fill_mode;
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* types */
+		len = tree->path->types->len;
+		memcpy (buffer, tree->path->types->data, len);
+		buffer += tree->path->types->len;
+		*sizeFilled += len;
+		/* points */
+		len = tree->path->points->len * sizeof (GpPointF);
+		memcpy (buffer, tree->path->points->data, len);
+		buffer += len;
+		*sizeFilled += len;
+	} else {
+		/* tag */
+		BYTE *original = buffer;
+		guint32 temp = REGION_TAG_TREE;
+		int len = sizeof (guint32);
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* operation */
+		temp = tree->mode;
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* serialize branch 1 (size + branch) */
+		temp = gdip_region_get_tree_size (tree->branch1);
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		if (!gdip_region_serialize_tree (tree->branch1, buffer, bufferSize - (buffer - original), sizeFilled))
+			return FALSE;
+		buffer += temp;
+		/* serialize branch 2 (size + branch)  */
+		temp = gdip_region_get_tree_size (tree->branch2);
+		memcpy (buffer, &temp, len);
+		buffer += len;
+		*sizeFilled += len;
+		if (!gdip_region_serialize_tree (tree->branch2, buffer, bufferSize - (buffer - original), sizeFilled))
+			return FALSE;
+	}
+	return TRUE;
+}
+
+
+/*
+ * gdip_region_transform_tree:
+ * @tree: a GpPathTree
+ * @matrix: the GpMatrix to apply to the tree
+ *
+ * Recursively apply the @matrix to the @tree.
+ */
+GpStatus
+gdip_region_transform_tree (GpPathTree *tree, GpMatrix *matrix)
+{
+	if (tree->path) {
+		return GdipTransformPath (tree->path, matrix);
+	} else {
+		GpStatus status;
+		status = gdip_region_transform_tree (tree->branch1, matrix);
+		if (status == Ok)
+			status = gdip_region_transform_tree (tree->branch2, matrix);
+		return status;
+	}
+}
+
+
+/*
+ * gdip_region_translate_tree:
+ * @tree: a GpPathTree
+ * @dx: the delta x to apply to each point
+ * @dy: the delta y to apply to each point 
+ *
+ * Recursively apply the @dx, @dy translation to each point, of each path, 
+ * in the @tree.
+ */
+void
+gdip_region_translate_tree (GpPathTree *tree, float dx, float dy)
+{
+	if (tree->path) {
+		int i;
+		for (i = 0; i < tree->path->count; i++) {
+			GpPointF *point = &g_array_index (tree->path->points, GpPointF, i);
+			point->X += dx;
+			point->Y += dy;
+		}
+	} else {
+		gdip_region_translate_tree (tree->branch1, dx, dy);
+		gdip_region_translate_tree (tree->branch2, dx, dy);
+	}
+}

Added: trunk/libgdiplus/src/region-path-tree.h
===================================================================
--- trunk/libgdiplus/src/region-path-tree.h	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region-path-tree.h	2006-03-25 16:33:37 UTC (rev 58520)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ * Authors:
+ *          Sebastien Pouliot  <sebastien at ximian.com>
+ */
+
+#ifndef _REGION_PATH_TREE_H_
+#define _REGION_PATH_TREE_H_
+
+#include "gdip.h"
+
+/* internal (private) API for regions path trees */
+
+#define REGION_TAG_PATH		1
+#define REGION_TAG_TREE		2
+
+void gdip_region_clear_tree (GpPathTree *tree);
+void gdip_region_copy_tree (GpPathTree *source, GpPathTree *dest);
+
+UINT gdip_region_get_tree_size (GpPathTree *tree);
+BOOL gdip_region_deserialize_tree (BYTE *data, int size, GpPathTree *tree);
+BOOL gdip_region_serialize_tree (GpPathTree *tree, BYTE *buffer, UINT bufferSize, UINT *sizeFilled);
+
+void gdip_region_translate_tree (GpPathTree *tree, float dx, float dy);
+GpStatus gdip_region_transform_tree (GpPathTree *tree, GpMatrix *matrix);
+
+#endif /* _REGION_PATH_TREE_H_ */

Modified: trunk/libgdiplus/src/region.c
===================================================================
--- trunk/libgdiplus/src/region.c	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region.c	2006-03-25 16:33:37 UTC (rev 58520)
@@ -23,9 +23,8 @@
  *
  */
 
-#include "gdip.h"
+#include "region.h"
 
-
 /*
 	Helper functions
 */
@@ -94,8 +93,14 @@
 	if (!region)
 		return FALSE;
 
-	if (region->type == RegionTypePath)
-		return (region->path->count == 0);
+	if (region->type == RegionTypePath) {
+		/* check for an existing, but empty, path list */
+		if (!region->tree)
+			return TRUE;
+		if (region->tree->path)
+			return (region->tree->path->count == 0);
+		return FALSE;
+	}
 
 	if (!region->rects || (region->cnt == 0))
 		return TRUE;
@@ -104,6 +109,15 @@
 	return ((rect.Width == 0) || (rect.Height == 0));
 }
 
+static BOOL
+gdip_is_rect_infinite (GpRectF *rect)
+{
+	return ((rect->X == REGION_INFINITE_POSITION) && 
+		(rect->Y == REGION_INFINITE_POSITION) &&
+		(rect->Width == REGION_INFINITE_LENGTH) && 
+		(rect->Height == REGION_INFINITE_LENGTH));
+}
+
 BOOL
 gdip_is_InfiniteRegion (GpRegion *region)
 {
@@ -111,12 +125,14 @@
 	case RegionTypeRectF:
 		if (region->cnt != 1)
 	              return FALSE;
-		if (region->rects->X == -4194304 && region->rects->Y == -4194304 &&
-			region->rects->Width == 8388608 &&  region->rects->Height == 8388608)
-			return TRUE;
-		break;
+		return gdip_is_rect_infinite (region->rects);
 	case RegionTypePath:
-		/* TODO - check for a infinite path. Note that we can't use the path bounds for this! */
+		/* FIXME: incomplete and not 100% accurate (curves) - but cover the most common case */
+		if (region->tree && region->tree->path && (region->tree->path->count == 4)) {
+			GpRectF bounds;
+			if (GdipGetPathWorldBounds (region->tree->path, &bounds, NULL, NULL) != Ok)
+				return gdip_is_rect_infinite (&bounds);
+		}
 		break;
 	default:
 		g_warning ("unknown type %d", region->type);
@@ -226,36 +242,51 @@
 gdip_clear_region (GpRegion *region)
 {
 	region->type = RegionTypeEmpty;
-	region->cnt = 0;
+
         if (region->rects) {
                 GdipFree (region->rects);
 		region->rects = NULL;
 	}
 
-	if (region->path) {
-		GdipDeletePath (region->path);
-		region->path = NULL;
+	if (region->tree) {
+		gdip_region_clear_tree (region->tree);
+                GdipFree (region->tree);
+		region->tree = NULL;
 	}
+
+	if (region->bitmap) {
+		gdip_region_bitmap_free (region->bitmap);
+		region->bitmap = NULL;
+	}
+
+	region->cnt = 0;
 }
 
 void
 gdip_copy_region (GpRegion *source, GpRegion *dest)
 {
 	dest->type = source->type;
-	dest->cnt = source->cnt;
 
 	if (source->rects) {
+		dest->cnt = source->cnt;
 	        dest->rects = (GpRectF *) GdipAlloc (sizeof (GpRectF) * source->cnt);
         	memcpy (dest->rects, source->rects, sizeof (GpRectF) * source->cnt);
 	} else {
 		dest->rects = NULL;
 	}
 
-	if (source->path) {
-		GdipClonePath (source->path, &dest->path);
+	if (source->tree) {
+		dest->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+		gdip_region_copy_tree (source->tree, dest->tree);
 	} else {
-		dest->path = NULL;
+		dest->tree = NULL;
 	}
+
+	if (source->bitmap) {
+		dest->bitmap = gdip_region_bitmap_clone (source->bitmap);
+	} else {
+		dest->bitmap = NULL;
+	}
 }
 
 /* convert a rectangle-based region to a path based region */
@@ -269,18 +300,30 @@
 	if (!region || (region->cnt == 0) || (region->type != RegionTypeRectF))
 		return;
 
-	GdipCreatePath (FillModeAlternate, &region->path);
+	region->type = RegionTypePath;
+	region->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+
+	GdipCreatePath (FillModeAlternate, &region->tree->path);
+	/* all rectangles are converted into a single path */
 	for (i = 0, rect = region->rects; i < region->cnt; i++, rect++) {
-		/* convert existing rectangles into a path */
-		GdipAddPathRectangle (region->path, rect->X, rect->Y, rect->Width, rect->Height);
+		GdipAddPathRectangle (region->tree->path, rect->X, rect->Y, rect->Width, rect->Height);
 	}
 
 	if (region->rects) {
 		GdipFree (region->rects);
 		region->rects = NULL;
 	}
-	region->cnt = 0;
+}
+
+/*
+ * Create a region (path-tree) from a path.
+ */
+static GpStatus
+gdip_region_create_from_path (GpRegion *region, GpPath *path)
+{
 	region->type = RegionTypePath;
+	region->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+	return GdipClonePath (path, &region->tree->path);
 }
 
 GpStatus
@@ -293,7 +336,8 @@
 	result->type = type;
 	result->cnt = 0;
         result->rects = NULL;
-        result->path = NULL;
+        result->tree = NULL;
+        result->bitmap = NULL;
 
         switch (type) {
         case RegionTypeRect:
@@ -309,7 +353,7 @@
 		/* note: GdipSetInfinite converts type to RegionTypeRectF */
                 break;
 	case RegionTypePath:
-		GdipClonePath ((GpPath*)src, &result->path);
+		gdip_region_create_from_path (result, (GpPath*)src);
 		break;
         default:
 		g_warning ("unknown type %d", result->type);
@@ -357,9 +401,6 @@
 GdipCreateRegionRgnData (GDIPCONST BYTE *regionData, int size, GpRegion **region)
 {
 	GpRegion *result;
-	GpRectF *rect;
-	GpPointF *points;
-	guint32 i, *count, *mode;
 
 	if (!region || !regionData || (size < 8))
 		return InvalidParameter;
@@ -368,23 +409,30 @@
 	result->type = *(guint32*) regionData;
 	result->cnt = 0;
         result->rects = NULL;
-	result->path = NULL;
-	count = (guint32*) (regionData + 4);
+	result->tree = NULL;
+        result->bitmap = NULL;
 
 	switch (result->type) {
-	case RegionTypeRectF:
-		if (*count != (size - 8) / sizeof (GpRectF))
+	case RegionTypeRectF: {
+		guint32 count = (guint32) *(regionData + 4);
+		GpRectF *rect;
+		int i;
+
+		if (count != (size - 8) / sizeof (GpRectF))
 			return InvalidParameter;
 
-		for (i = 0, rect = (GpRectF*)(regionData + 8); i < *count; i++, rect++)
+		for (i = 0, rect = (GpRectF*)(regionData + 8); i < count; i++, rect++)
 	                gdip_add_rect_to_array (&result->rects, &result->cnt, rect);
+		}
 		break;
-	case RegionTypePath:
-		if (size < 12)
+	case RegionTypePath: {
+		if (size < 16)
 			return InvalidParameter;
-		mode = (guint32*) (regionData + 8);
-		points = (GpPointF*) (regionData + 12 + *count);
-		GdipCreatePath2 (points, (regionData + 12), *count, *mode, &result->path);
+
+		result->tree = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+		if (!gdip_region_deserialize_tree ((BYTE*)(regionData + 4), (size - 4), result->tree))
+			return InvalidParameter;
+		}
 		break;
 	default:
 		g_warning ("unknown type %d", result->type);
@@ -435,8 +483,8 @@
 	gdip_clear_region (region);
 	region->type = RegionTypeRectF;
 
-        rect.X = rect.Y = -4194304;
-        rect.Width = rect.Height= 8388608;
+        rect.X = rect.Y = REGION_INFINITE_POSITION;
+        rect.Width = rect.Height = REGION_INFINITE_LENGTH;
 
         gdip_add_rect_to_array (&region->rects, &region->cnt,  &rect);
         return Ok;
@@ -491,8 +539,6 @@
 }
 
 
-
-
 /* Exclude */
 void
 gdip_combine_exclude (GpRegion *region, GpRectF *rtrg, int cntt)
@@ -932,14 +978,16 @@
 GpStatus
 GdipCombineRegionPath (GpRegion *region, GpPath *path, CombineMode combineMode)
 {
+	GpRegionBitmap *path_bitmap, *result;
+	GpPathTree* tmp;
+
 	if (!region || !path)
 		return InvalidParameter;
 
 	/* special case #1 - replace */
 	if (combineMode == CombineModeReplace) {
 		gdip_clear_region (region);
-		region->type = RegionTypePath;
-		GdipClonePath (path, &region->path);
+		gdip_region_create_from_path (region, path);
 		return Ok;
 	}
 
@@ -951,8 +999,7 @@
 		case CombineModeXor:
 			/* this is like "adding" the path */
 			gdip_clear_region (region);
-			region->type = RegionTypePath;
-			GdipClonePath (path, &region->path);
+			gdip_region_create_from_path (region, path);
 			break;
 		default:
 			/* Intersect and Exclude are no-op on an empty region */
@@ -975,8 +1022,7 @@
 		case CombineModeIntersect:
 			/* Intersection with infinity is the path itself */
 			gdip_clear_region (region);
-			region->type = RegionTypePath;
-			GdipClonePath (path, &region->path);
+			gdip_region_create_from_path (region, path);
 			return Ok;
 		default:
 			/* Xor and Exclude must be treated as a "normal" case */
@@ -987,22 +1033,104 @@
 	if (region->type == RegionTypeRectF)
 		gdip_region_convert_to_path (region);
 
-	/* TODO combine both complex regions */
-        return NotImplemented;
+	/* make sure the region's bitmap is available */
+	gdip_region_bitmap_ensure (region);
+	g_assert (region->bitmap);
+
+	/* create a bitmap for the path to combine into the region */
+	path_bitmap = gdip_region_bitmap_from_path (path);
+
+	result = gdip_region_bitmap_combine (region->bitmap, path_bitmap, combineMode);
+	gdip_region_bitmap_free (path_bitmap);
+	if (!result)
+		return NotImplemented;
+
+	gdip_region_bitmap_free (region->bitmap);
+	region->bitmap = result;
+
+	/* add a copy of path into region1 tree */
+	tmp = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+	tmp->mode = combineMode;
+	tmp->path = NULL;
+	tmp->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+	tmp->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+
+	if (region->tree->path) {
+		tmp->branch1->path = region->tree->path;
+	} else {
+		gdip_region_copy_tree (region->tree, tmp->branch1);
+	}
+
+	GdipClonePath (path, &tmp->branch2->path);
+
+	region->tree = tmp;
+	return Ok;
 }
 
 
+static GpStatus
+gdip_combine_pathbased_region (GpRegion *region1, GpRegion *region2, CombineMode combineMode)
+{
+	GpRegionBitmap *result;
+	GpPathTree* tmp;
+
+	/* if not available, construct the bitmaps for both regions */
+	gdip_region_bitmap_ensure (region1);
+	gdip_region_bitmap_ensure (region2);
+	if (!region1->bitmap || !region2->bitmap)
+		return OutOfMemory;
+
+	result = gdip_region_bitmap_combine (region1->bitmap, region2->bitmap, combineMode);
+	if (!result)
+		return NotImplemented;
+	gdip_region_bitmap_free (region1->bitmap);
+	region1->bitmap = result;
+
+	/* add a copy of region2 tree into region1 tree */
+	tmp = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+	tmp->mode = combineMode;
+	tmp->path = NULL;
+	tmp->branch1 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+	tmp->branch2 = (GpPathTree*) GdipAlloc (sizeof (GpPathTree));
+
+	if (region1->tree->path) {
+		tmp->branch1->path = region1->tree->path;
+	} else {
+		gdip_region_copy_tree (region1->tree, tmp->branch1);
+	}
+
+	if (region2->tree->path) {
+		GdipClonePath (region2->tree->path, &tmp->branch2->path);
+	} else {
+		gdip_region_copy_tree (region2->tree, tmp->branch2);
+	}
+
+	GdipFree (region1->tree);
+	region1->tree = tmp;
+	return Ok;
+}
+
+
 GpStatus
 GdipCombineRegionRegion (GpRegion *region,  GpRegion *region2, CombineMode combineMode)
 {
         if (!region || !region2)
                 return InvalidParameter;
 
+	/* special case to deal with copying empty and infinity regions */
+	/* CombineModeReplace is used by Graphics clipping */
+	if (combineMode == CombineModeReplace) {
+		GdipSetEmpty (region);
+		gdip_copy_region (region2, region);
+		return Ok;
+	}
+
 	if (region->type == RegionTypePath) {
 		gdip_region_convert_to_path (region2);
-		return GdipCombineRegionPath (region, region2->path, combineMode);
+		return gdip_combine_pathbased_region (region, region2, combineMode);
 	} else if (region2->type == RegionTypePath) {
-		return GdipCombineRegionPath (region, region2->path, combineMode);
+		gdip_region_convert_to_path (region);
+		return gdip_combine_pathbased_region (region, region2, combineMode);
 	}
 
 	/* at this stage we are sure that BOTH region and region2 are rectangle 
@@ -1024,19 +1152,8 @@
         case CombineModeXor:
                 gdip_combine_xor (region, region2->rects, region2->cnt);
                 break;
-	case CombineModeReplace: { /* Used by Graphics clipping */
-		int i;
-		GpRectF* rect;
-		GdipSetEmpty (region);
-		
-		for (i = 0, rect = region2->rects; i < region2->cnt; i++, rect++)
-			gdip_add_rect_to_array (&region->rects, &region->cnt, rect);
-	
-		break;
-	}
         default:
                return NotImplemented;
-
         }
 
         return Ok;
@@ -1048,8 +1165,25 @@
         if (!region || !graphics || !rect)
                 return InvalidParameter;
 
-        gdip_get_bounds (region->rects , region->cnt, rect);
+	if (region->type == RegionTypePath) {
+		GpRect bounds;
 
+		gdip_region_bitmap_ensure (region);
+		if (!region->bitmap)
+			return OutOfMemory;
+
+		/* base the bounds on the reduced bitmap of the paths */
+		gdip_region_bitmap_get_smallest_rect (region->bitmap, &bounds);
+
+		/* small loss of precision when converting the bitmap coord to float */
+		rect->X = bounds.X;
+		rect->Y = bounds.Y;
+		rect->Width = bounds.Width;
+		rect->Height = bounds.Height;
+	} else {
+	        gdip_get_bounds (region->rects , region->cnt, rect);
+	}
+
         return Ok;
 }
 
@@ -1079,12 +1213,18 @@
 GpStatus
 GdipIsVisibleRegionPoint (GpRegion *region, float x, float y, GpGraphics *graphics, BOOL *result)
 {
-
         if (!region || !result)
                 return InvalidParameter;
 
-        *result = gdip_is_Point_in_RectFs_Visible (x, y, region->rects, region->cnt);
+	if (region->type == RegionTypePath) {
+		gdip_region_bitmap_ensure (region);
+		g_assert (region->bitmap);
 
+		*result = gdip_region_bitmap_is_point_visible (region->bitmap, x, y);
+	} else {
+		*result = gdip_is_Point_in_RectFs_Visible (x, y, region->rects, region->cnt);
+	}
+
         return Ok;
 }
 
@@ -1092,15 +1232,7 @@
 GpStatus
 GdipIsVisibleRegionPointI (GpRegion *region, int x, int y, GpGraphics *graphics, BOOL *result)
 {
-        float xf, yf;
-
-        if (!region || !result)
-                return InvalidParameter;
-
-        xf = x;
-        yf = y;
-
-        return GdipIsVisibleRegionPoint (region, xf, yf, graphics, result);
+        return GdipIsVisibleRegionPoint (region, x, y, graphics, result);
 }
 
 
@@ -1108,8 +1240,6 @@
 GdipIsVisibleRegionRect (GpRegion *region, float x, float y, float width, float height, GpGraphics *graphics, BOOL *result)
 {
         BOOL found = FALSE;
-        float posy, posx;
-        GpRectF recthit;
 
         if (!region || !result)
                 return InvalidParameter;
@@ -1119,19 +1249,37 @@
                 return Ok;
         }
 
-        recthit.X = x; recthit.Y = y;
-        recthit.Width = width; recthit.Height = height;
+	if (region->type == RegionTypePath) {
+		GpRect rect;
 
-        /* Any point of intersection ?*/
-        for (posy = 0; posy < recthit.Height && found == FALSE; posy++) {
+		rect.X = x;
+		rect.Y = y;
+		rect.Width = width;
+		rect.Height = height;
+		
+		gdip_region_bitmap_ensure (region);
+		g_assert (region->bitmap);
 
-                for (posx = 0; posx < recthit.Width ; posx++) {
-                        if (gdip_is_Point_in_RectFs_Visible (recthit.X + posx , recthit.Y + posy, region->rects, region->cnt) == TRUE) {
-                                found = TRUE;
-                                break;
-                        }
+		found = gdip_region_bitmap_is_rect_visible (region->bitmap, &rect);
+	} else {
+		float posy, posx;
+		GpRectF recthit;
+
+		recthit.X = x;
+		recthit.Y = y;
+		recthit.Width = width;
+		recthit.Height = height;
+
+		/* Any point of intersection ?*/
+		for (posy = 0; posy < recthit.Height && found == FALSE; posy++) {
+			for (posx = 0; posx < recthit.Width ; posx++) {
+				if (gdip_is_Point_in_RectFs_Visible (recthit.X + posx , recthit.Y + posy, region->rects, region->cnt) == TRUE) {
+					found = TRUE;
+					break;
+				}
+			}
 		}
-        }
+	}
 
         *result = found;
         return Ok;
@@ -1141,37 +1289,138 @@
 GpStatus
 GdipIsVisibleRegionRectI (GpRegion *region, int x, int y, int width, int height, GpGraphics *graphics, BOOL *result)
 {
-        float fx, fy, fw, fh;
+        return GdipIsVisibleRegionRect (region, x, y, width, height, graphics, result);
+}
 
-        if (!region || !result)
-                return InvalidParameter;
 
-        fx = x; fy = y;
-        fh = height; fw = width;
+/*
+ * In System.Drawing it is often impossible to specify a 'null' matrix. 
+ * Instead we supply an empty matrix (i.e. new Matrix ()). However this
+ * "empty" matrix can cause a lot of extra calculation in libgdiplus
+ * (e.g. invalidating the bitmap) unless we consider it as a special case.
+ */
+static BOOL
+is_matrix_empty (GpMatrix* matrix)
+{
+	float elements[6];
 
-        return GdipIsVisibleRegionRect (region, fx, fy, fw, fh, graphics, result);
+	if (!matrix)
+		return TRUE;
+
+	if (GdipGetMatrixElements (matrix, elements) != Ok)
+		return FALSE;
+
+	/* compare the matrix elements with the empty (no-op) version */
+	return ((elements [0] == 1.0f) && (elements [1] == 0.0f) && (elements [2] == 0.0f) &&
+		(elements [3] == 1.0f) && (elements [4] == 0.0f) && (elements [5] == 0.0f));
 }
 
 
 GpStatus
 GdipGetRegionScansCount (GpRegion *region, int* count, GpMatrix* matrix)
 {
+	GpRegion *work;
+	GpStatus status;
+
         if (!region || !count)
                 return InvalidParameter;
 
-        *count = region->cnt;
-        return Ok;
+	/* apply any user supplied matrix transformation */
+	if (!is_matrix_empty (matrix)) {
+		int i;
+
+		/* the matrix doesn't affect the original region - only the result */
+		status = GdipCloneRegion (region, &work);
+		if (status != Ok)
+			return status;
+
+		/* if required convert into a path-based region */
+		if (work->type != RegionTypePath)
+			gdip_region_convert_to_path (work);
+
+		/* transform all the paths */
+		status = gdip_region_transform_tree (work->tree, matrix);
+		if (status != Ok) {
+			GdipDeleteRegion (work);
+			return status;
+		}
+		/* note: any existing bitmap has been invalidated */
+		gdip_region_bitmap_invalidate (work);
+	} else {
+		work = region;
+	}
+
+	if (work->type == RegionTypePath) {
+		/* ensure the bitmap is usable */
+		gdip_region_bitmap_ensure (work);
+
+		/* check if region is too large to render */
+		if (work->bitmap)
+			*count = gdip_region_bitmap_get_scans (work->bitmap, NULL, -1);
+		else
+			*count = 0;
+	} else {
+	        *count = work->cnt;
+	}
+
+	/* delete the clone */
+	if (work != region)
+		GdipDeleteRegion (work);
+	return Ok;
 }
 
 GpStatus
 GdipGetRegionScans (GpRegion *region, GpRectF* rects, int* count, GpMatrix* matrix)
 {
+	GpRegion *work;
+	GpStatus status;
+
         if (!region || !rects|| !count)
                 return InvalidParameter;
 
-        memcpy (rects, region->rects, sizeof (GpRectF) * *count);
-        *count = region->cnt;
-        return Ok;
+	/* apply any user supplied matrix transformation */
+	if (!is_matrix_empty (matrix)) {
+		int i;
+
+		/* the matrix doesn't affect the original region - only the result */
+		status = GdipCloneRegion (region, &work);
+		if (status != Ok)
+			return status;
+
+		/* if required convert into a path-based region */
+		if (work->type != RegionTypePath)
+			gdip_region_convert_to_path (work);
+
+		/* transform all the paths */
+		status = gdip_region_transform_tree (work->tree, matrix);
+		if (status != Ok) {
+			GdipDeleteRegion (work);
+			return status;
+		}
+		/* note: any existing bitmap has been invalidated */
+		gdip_region_bitmap_invalidate (work);
+	} else {
+		work = region;
+	}
+
+	if (region->type == RegionTypePath) {
+		/* ensure the bitmap is usable */
+		gdip_region_bitmap_ensure (work);
+
+		/* check if region is too large to render */
+		if (work->bitmap)
+			*count = gdip_region_bitmap_get_scans (work->bitmap, rects, *count);
+		else
+			*count = 0;
+	} else {
+	        memcpy (rects, work->rects, sizeof (GpRectF) * *count);
+	        *count = work->cnt;
+	}
+
+	/* delete the clone */
+	if (work != region)
+		GdipDeleteRegion (work);
+	return Ok;
 }
 
 GpStatus
@@ -1183,6 +1432,30 @@
         if (!region || !region2 || !graphics || !result)
                 return InvalidParameter;
 
+	/* quick case: same pointer == same region == equals */
+	if (region == region2) {
+		*result = TRUE;
+		return Ok;
+	}
+
+	if ((region->type == RegionTypePath) || (region2->type == RegionTypePath)) {
+		/* if required convert one region to a path based region */
+		if (region->type != RegionTypePath)
+			gdip_region_convert_to_path (region);
+		gdip_region_bitmap_ensure (region);
+		g_assert (region->bitmap);
+
+		if (region2->type != RegionTypePath)
+			gdip_region_convert_to_path (region2);
+
+		gdip_region_bitmap_ensure (region2);
+		g_assert (region2->bitmap);
+
+		*result = gdip_region_bitmap_compare (region->bitmap, region2->bitmap);
+                return Ok;
+	}
+
+	/* rectangular-based region quality test */
         if (region->cnt != region2->cnt) {
                 *result = FALSE;
                 return Ok;
@@ -1205,21 +1478,20 @@
 GpStatus
 GdipTranslateRegion (GpRegion *region, float dx, float dy)
 {
-        int i;
-
         if (!region)
                 return InvalidParameter;
 
 	if (region->type == RegionTypePath) {
-		GpPointF *point;
-		for (i = 0; i < region->path->count; i++) {
-			point = &g_array_index (region->path->points, GpPointF, i);
-			point->X += dx;
-			point->Y += dy;
+		gdip_region_translate_tree (region->tree, dx, dy);
+		/* any existing bitmap is still valid _if_ we update it's origin */
+		if (region->bitmap) {
+			region->bitmap->X += dx;
+			region->bitmap->Y += dy;
 		}
 	} else if ((region->type == RegionTypeRectF) && region->rects) {
+	        int i;
 	        GpRectF *rect;
-                for (i = 0, rect=region->rects ; i < region->cnt; i++, rect++) {
+                for (i = 0, rect = region->rects ; i < region->cnt; i++, rect++) {
                         rect->X += dx;
                         rect->Y += dy;
                 }
@@ -1231,19 +1503,32 @@
 GpStatus
 GdipTranslateRegionI (GpRegion *region, int dx, int dy)
 {
-        float fx, fy;
-
-        if (!region)
-                return InvalidParameter;
-
-        fx = dx; fy = dy;
-        return  GdipTranslateRegion (region, fx, fy);
+        return GdipTranslateRegion (region, dx, dy);
 }
 
 GpStatus
 GdipTransformRegion (GpRegion *region, GpMatrix *matrix)
 {
-        return NotImplemented;
+	GpStatus status;
+
+	if (!region || !matrix)
+		return InvalidParameter;
+
+	/* don't (possibly) convert to a bitmap if the matrix is empty (a no-op) */
+	if (is_matrix_empty (matrix))
+		return Ok;
+
+	/* most matrix operations would change the rectangles into path so we always preempt this */
+	if (region->type != RegionTypePath)
+		gdip_region_convert_to_path (region);
+
+	/* apply the same transformation matrix to all paths */
+	status = gdip_region_transform_tree (region->tree, matrix);
+
+	/* invalidate the bitmap so it will get re-created on the next gdip_region_bitmap_ensure call */
+	gdip_region_bitmap_invalidate (region);
+
+        return status;
 }
 
 GpStatus
@@ -1274,10 +1559,24 @@
  *
  * Type 3 (RegionTypePath), variable size
  *	guint32 RegionType	Always 3
- *	guint32 Count		0-2^32
- *	GpFillMode FillMode	
- *	guint8[Count] Types	
- *	GpPointF[Count] Points
+ *	GpPathTree tree
+ *
+ * where GpPathTree is
+ *	guint32 Tag		1 = Path, 2 = Tree
+ *	data[n]
+ *
+ *	where data is for tag 1 (Path)
+ *		guint32 Count		0-2^32
+ *		GpFillMode FillMode	
+ *		guint8[Count] Types	
+ *		GpPointF[Count] Points
+ *	or
+ *	where data is for tag 2 (Tree)
+ *		guint32 Operation	see CombineMode
+ *		guint32 Size1		0-2^32
+ *		byte[Size1]		branch #1
+ *		guint32 Size2		0-2^32
+ *		byte[Size2]		branch #2
  */
 
 GpStatus
@@ -1291,9 +1590,8 @@
 		*bufferSize = (sizeof (guint32) * 2) + (region->cnt * sizeof (GpRectF));
 		break;
 	case RegionTypePath:
-		*bufferSize = (sizeof (guint32) * 2) + sizeof (GpFillMode) + 
-			(region->path->count * sizeof (guint8)) +
-			(region->path->count * sizeof (GpPointF));
+		/* regiontype, tree */
+		*bufferSize = sizeof (guint32) + gdip_region_get_tree_size (region->tree);
 		break;
 	default:
 		g_warning ("unknown type %d", region->type);
@@ -1308,6 +1606,7 @@
 {
 	GpStatus status;
 	UINT size;
+	int len;
 
 	if (!region || !buffer || !sizeFilled)
 		return InvalidParameter;
@@ -1318,28 +1617,32 @@
 	if (size > bufferSize)
 		return InsufficientBuffer;
 
-	memcpy (buffer, &region->type, sizeof (guint32));
-	buffer += sizeof (guint32);
+	/* type of region */
+	len = sizeof (guint32);
+	memcpy (buffer, &region->type, len);
+	buffer += len;
+	*sizeFilled += len;
+
 	switch (region->type) {
 	case RegionTypeRectF:
-		memcpy (buffer, &region->cnt, sizeof (guint32));
-		buffer += sizeof (guint32);
-		memcpy (buffer, region->rects, sizeof (GpRectF) * (region->cnt));
+		/* count (# rectangles) */
+		memcpy (buffer, &region->cnt, len);
+		buffer += len;
+		*sizeFilled += len;
+		/* rectangles */
+		len = sizeof (GpRectF) * (region->cnt);
+		memcpy (buffer, region->rects, len);
+		*sizeFilled += len;
 		break;
 	case RegionTypePath:
-		memcpy (buffer, &region->path->count, sizeof (guint32));
-		buffer += sizeof (guint32);
-		memcpy (buffer, &region->path->fill_mode, sizeof (GpFillMode));
-		buffer += sizeof (GpFillMode);
-		memcpy (buffer, region->path->types->data, region->path->types->len);
-		buffer += region->path->types->len;
-		memcpy (buffer, region->path->points->data, region->path->points->len * sizeof (GpPointF));
+		bufferSize -= len;
+		if (!gdip_region_serialize_tree (region->tree, buffer, bufferSize, sizeFilled))
+			return InsufficientBuffer;
 		break;
 	default:
 		g_warning ("unknown type %d", region->type);
 		return NotImplemented;
 	}
 
-	*sizeFilled = size;
 	return Ok;
 }

Added: trunk/libgdiplus/src/region.h
===================================================================
--- trunk/libgdiplus/src/region.h	2006-03-25 15:39:48 UTC (rev 58519)
+++ trunk/libgdiplus/src/region.h	2006-03-25 16:33:37 UTC (rev 58520)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ *
+ * Authors:
+ *          Sebastien Pouliot  <sebastien at ximian.com>
+ */
+
+#ifndef _REGION_H_
+#define _REGION_H_
+
+#include "gdip.h"
+#include "region-bitmap.h"
+#include "region-path-tree.h"
+
+/* internal (private) API for regions */
+
+#define REGION_INFINITE_POSITION	-4194304
+#define REGION_INFINITE_LENGTH		8388608
+
+BOOL gdip_is_InfiniteRegion (GpRegion *region);
+BOOL gdip_is_Point_in_RectF_inclusive (float x, float y, GpRectF* rect);
+BOOL gdip_is_Point_in_RectF_inclusive (float x, float y, GpRectF* rect);
+
+void gdip_clear_region (GpRegion *region);
+void gdip_copy_region (GpRegion *source, GpRegion *dest);
+
+#endif /* _REGION_H_ */



More information about the Mono-patches mailing list