[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, ®ion->path);
+ region->type = RegionTypePath;
+ region->tree = (GpPathTree *) GdipAlloc (sizeof (GpPathTree));
+
+ GdipCreatePath (FillModeAlternate, ®ion->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, ®ion->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 (®ion->rects, ®ion->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, ®ion->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, ®ion->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, ®ion->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 (®ion->rects, ®ion->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, ®ion->type, sizeof (guint32));
- buffer += sizeof (guint32);
+ /* type of region */
+ len = sizeof (guint32);
+ memcpy (buffer, ®ion->type, len);
+ buffer += len;
+ *sizeFilled += len;
+
switch (region->type) {
case RegionTypeRectF:
- memcpy (buffer, ®ion->cnt, sizeof (guint32));
- buffer += sizeof (guint32);
- memcpy (buffer, region->rects, sizeof (GpRectF) * (region->cnt));
+ /* count (# rectangles) */
+ memcpy (buffer, ®ion->cnt, len);
+ buffer += len;
+ *sizeFilled += len;
+ /* rectangles */
+ len = sizeof (GpRectF) * (region->cnt);
+ memcpy (buffer, region->rects, len);
+ *sizeFilled += len;
break;
case RegionTypePath:
- memcpy (buffer, ®ion->path->count, sizeof (guint32));
- buffer += sizeof (guint32);
- memcpy (buffer, ®ion->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