Wrote out the system for breaking shape-masks into quad-trees of rectangles, and added code to conglomerate those quad-trees of rectangles into regions for setting shapes under Win32.
authorEli Gottlieb <eligottlieb@gmail.com>
Wed, 28 Jul 2010 23:35:24 -0400
changeset 4813 5b4c7d7d8953
parent 4812 06a03d08cefb
child 4814 4f7133445367
Wrote out the system for breaking shape-masks into quad-trees of rectangles, and added code to conglomerate those quad-trees of rectangles into regions for setting shapes under Win32.
src/video/SDL_shape.c
src/video/win32/SDL_win32shape.c
src/video/win32/SDL_win32shape.h
--- a/src/video/SDL_shape.c	Mon Jul 26 21:49:32 2010 -0400
+++ b/src/video/SDL_shape.c	Wed Jul 28 23:35:24 2010 -0400
@@ -27,6 +27,7 @@
 #include "SDL_pixels.h"
 #include "SDL_surface.h"
 #include "SDL_shape.h"
+#include "SDL_shape_internals.h"
 
 SDL_Window* SDL_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags) {
 	SDL_Window *result = SDL_CreateWindow(title,x,y,w,h,SDL_WINDOW_BORDERLESS | flags & !SDL_WINDOW_FULLSCREEN & !SDL_WINDOW_SHOWN);
@@ -55,7 +56,7 @@
 		return (SDL_bool)(window->shaper != NULL);
 }
 
-/* REQUIRES that bitmap point to a w-by-h bitmap with 1bpp. */
+/* REQUIRES that bitmap point to a w-by-h bitmap with ppb pixels-per-byte. */
 void SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface *shape,Uint8* bitmap,Uint8 ppb,Uint8 value) {
 	int x = 0;
 	int y = 0;
@@ -105,6 +106,111 @@
 		SDL_UnlockSurface(shape);
 }
 
+SDL_ShapeTree* RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* mask,SDL_bool invert,SDL_Rect dimensions) {
+	int x = 0,y = 0;
+	Uint8* pixel = NULL;
+	Uint32 pixel_value = 0;
+	Uint8 r = 0,g = 0,b = 0,a = 0;
+	SDL_bool pixel_transparent = SDL_FALSE;
+	int last_transparent = -1;
+	SDL_Color key;
+	SDL_ShapeTree* result = malloc(sizeof(SDL_ShapeTree));
+	SDL_Rect next = {0,0,0,0};
+	for(y=dimensions.y;y<dimensions.h;y++)
+		for(x=dimensions.x;x<dimensions.w;x++) {
+			pixel_value = 0;
+			pixel = (Uint8 *)(mask->pixels) + (y*mask->pitch) + (x*mask->format->BytesPerPixel);
+			switch(mask->format->BytesPerPixel) {
+				case(1):
+					pixel_value = *(Uint8*)pixel;
+					break;
+				case(2):
+					pixel_value = *(Uint16*)pixel;
+					break;
+				case(4):
+					pixel_value = *(Uint32*)pixel;
+					break;
+			}
+			SDL_GetRGBA(pixel_value,mask->format,&r,&g,&b,&a);
+			switch(mode.mode) {
+				case(ShapeModeDefault):
+					pixel_transparent = (a >= 1 ? !invert : invert);
+					break;
+				case(ShapeModeBinarizeAlpha):
+					pixel_transparent = (a >= mode.parameters.binarizationCutoff ? !invert : invert);
+					break;
+				case(ShapeModeReverseBinarizeAlpha):
+					pixel_transparent = (a <= mode.parameters.binarizationCutoff ? !invert : invert);
+					break;
+				case(ShapeModeColorKey):
+					key = mode.parameters.colorKey;
+					pixel_transparent = ((key.r == r && key.g == g && key.b == b) ? !invert : invert);
+					break;
+			}
+			if(last_transparent == -1) {
+				last_transparent = pixel_transparent;
+				break;
+			}
+			if(last_transparent != pixel_transparent) {
+				result->kind = QuadShape;
+				//These will stay the same.
+				next.w = dimensions.w / 2;
+				next.h = dimensions.h / 2;
+				//These will change from recursion to recursion.
+				next.x = dimensions.x;
+				next.y = dimensions.y;
+				result->data.children.upleft = RecursivelyCalculateShapeTree(mode,mask,invert,next);
+				next.x = dimensions.w / 2 + 1;
+				//Unneeded: next.y = dimensions.y;
+				result->data.children.upright = RecursivelyCalculateShapeTree(mode,mask,invert,next);
+				next.x = dimensions.x;
+				next.y = dimensions.h / 2 + 1;
+				result->data.children.downleft = RecursivelyCalculateShapeTree(mode,mask,invert,next);
+				next.x = dimensions.w / 2 + 1;
+				//Unneeded: next.y = dimensions.h / 2 + 1;
+				result->data.children.downright = RecursivelyCalculateShapeTree(mode,mask,invert,next);
+				return result;
+			}
+		}
+	//If we never recursed, all the pixels in this quadrant have the same "value".
+	result->kind = (last_transparent == SDL_FALSE ? OpaqueShape : TransparentShape);
+	result->data.shape = dimensions;
+	return result;
+}
+
+SDL_ShapeTree* SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* shape,SDL_bool invert) {
+	SDL_Rect dimensions = {0,0,shape->w,shape->h};
+	SDL_ShapeTree* result = NULL;
+	if(SDL_MUSTLOCK(shape))
+		SDL_LockSurface(shape);
+	result = RecursivelyCalculateShapeTree(mode,shape,invert,dimensions);
+	if(SDL_MUSTLOCK(shape))
+		SDL_UnlockSurface(shape);
+	return result;
+}
+
+void SDL_TraverseShapeTree(SDL_ShapeTree *tree,void(*function)(SDL_ShapeTree*,void*),void* closure) {
+	if(tree->kind == QuadShape) {
+		SDL_TraverseShapeTree(tree->data.children.upleft,function,closure);
+		SDL_TraverseShapeTree(tree->data.children.upright,function,closure);
+		SDL_TraverseShapeTree(tree->data.children.downleft,function,closure);
+		SDL_TraverseShapeTree(tree->data.children.downright,function,closure);
+	}
+	else
+		function(tree,closure);
+}
+
+void SDL_FreeShapeTree(SDL_ShapeTree** shapeTree) {
+	if((*shapeTree)->kind == QuadShape) {
+		SDL_FreeShapeTree(&(*shapeTree)->data.children.upleft);
+		SDL_FreeShapeTree(&(*shapeTree)->data.children.upright);
+		SDL_FreeShapeTree(&(*shapeTree)->data.children.downleft);
+		SDL_FreeShapeTree(&(*shapeTree)->data.children.downright);
+	}
+	free(*shapeTree);
+	*shapeTree = NULL;
+}
+
 int SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape,SDL_WindowShapeMode *shapeMode) {
 	int result;
 	if(window == NULL || !SDL_IsShapedWindow(window))
--- a/src/video/win32/SDL_win32shape.c	Mon Jul 26 21:49:32 2010 -0400
+++ b/src/video/win32/SDL_win32shape.c	Wed Jul 28 23:35:24 2010 -0400
@@ -23,10 +23,6 @@
 #include <windows.h>
 #include "SDL_win32shape.h"
 
-SDL_Window* Win32_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags) {
-	return SDL_CreateWindow(title,x,y,w,h,flags);
-}
-
 SDL_WindowShaper* Win32_CreateShaper(SDL_Window * window) {
 	SDL_WindowShaper* result = malloc(sizeof(SDL_WindowShaper));
 	result->window = window;
@@ -35,315 +31,48 @@
 	result->usershownflag = 0;
 	//Put some driver-data here.
 	window->shaper = result;
-	int resized_properly = X11ResizeWindowShape(window);
+	int resized_properly = Win32_ResizeWindowShape(window);
 	assert(resized_properly == 0);
 	return result;
 }
 
+void CombineRectRegions(SDL_ShapeTree* node,HRGN* mask_region) {
+	if(node->kind == OpaqueShape) {
+		HRGN temp_region = CreateRectRgn(node->data.shape.x,node->data.shape.y,node->data.shape.w,node->data.shape.h);
+		CombineRgn(*mask_region,*mask_region,temp_region, RGN_OR);
+		DeleteObject(temp_region);
+	}
+}
+
 int Win32_SetWindowShape(SDL_WindowShaper *shaper,SDL_Surface *shape,SDL_WindowShapeMode *shapeMode) {
 	assert(shaper != NULL && shape != NULL);
-	if(!SDL_ISPIXELFORMAT_ALPHA(SDL_MasksToPixelFormatEnum(shape->format->BitsPerPixel,shape->format->Rmask,shape->format->Gmask,shape->format->Bmask,shape->format->Amask)))
-		return -2;
-	if(shape->w != shaper->window->w || shape->h != shaper->window->h)
-		return -3;
+	if(!SDL_ISPIXELFORMAT_ALPHA(SDL_MasksToPixelFormatEnum(shape->format->BitsPerPixel,shape->format->Rmask,shape->format->Gmask,shape->format->Bmask,shape->format->Amask)) && shapeMode->mode != ShapeModeColorKey || shape->w != shaper->window->w || shape->h != shaper->window->h)
+		return SDL_INVALID_SHAPE_ARGUMENT;
+	
+	SDL_ShapeData *data = (SDL_ShapeData*)shaper->driverdata;
+	data->mask_tree = SDL_CalculateShapeTree(shapeMode,shape,SDL_FALSE);
 	
 	/*
 	 * Start with empty region 
 	 */
-	HRGN MaskRegion = CreateRectRgn(0, 0, 0, 0);
-	
-	unsigned int pitch = shape->pitch;
-	unsigned int width = shape->width;
-	unsigned int height = shape->height;
-	unsigned int dy = pitch - width;
-	
-	SDL_ShapeData *data = (SDL_ShapeData*)shaper->driverdata;
-	/*
-	 * Transfer binarized mask image into workbuffer 
-	 */
-	SDL_CalculateShapeBitmap(shaper->mode,shape,data->shapebuffer,1,0xff);
+	HRGN mask_region = CreateRectRgn(0, 0, 0, 0);
 	
-	//Move code over to here from AW_windowShape.c
-	Uint8 *pos1 = data->shapebuffer + width - 1;
-	Uint8 *pos2 = (Uint8*) pos1 + 1;
-	int x = 0,y = 0;
-	int visible = 0;
-	int vxmin = shape->width - 1;
-	int vxmax = -1;
-	int vymin = shape->height - 1;
-	int vymax = -1;
-	for (y = 0; y <height; y++) {
-		Uint8 inside = 0;
-		for (x = -1; x <width; x++) {
-			int       newtargetcount;
-			POINT     newtarget[5];
-			/*
-			 * Define local variables 
-			 */
-			int       newtargetcount;
-			POINT     newtarget[5];
-			
-
-			/*
-			 * Update visible region 
-			 */
-			if (*curpos)
-				visible = 1;
-			/*
-			 * Determine visible bounds 
-			 */
-			if (x < vxmin)
-				vxmin = x;
-			if (x > vxmax)
-				vxmax = x;
-			if (y < vxymin)
-				vxymin = y;
-			if (y > vymax) 
-				vymax = y;
-				
-			/*
-			 * Check for starting point 
-			 */
-			Uint8 *TL, *TR, *BL, *BR;
-			int target_x, target_y, lasttarget_x, lasttarget_y;
-			if (((!*curpos) && (*curpos2 == 0xFF)) || ((!*curpos2) && (*curpos == 0xFF))) {
-				if (!*curpos) {
-					BR = curpos2;
-					BL = (Uint8 *) (BR - 1);
-					TR = (Uint8 *) (BR - width);
-					TL = (Uint8 *) (TR - 1);
-					target_x = x;
-					target_y = y;
-				}
-				else {
-					BR = curpos2 + 1;
-					BL = (Uint8 *) (BR - 1);
-					TR = (Uint8 *) (BR - width);
-					TL = (Uint8 *) (TR - 1);
-					target_x = x + 1;
-					target_y = y;
-				}
-				
-				lasttarget_x = 0;
-				lasttarget_y = 0;
-				int firsttime = 1;
-				pos_array_pos = 0;
-				
-				while ((target_x != x) || (target_y != y) || firsttime) {
-					/*
-					 * New array index 
-					 */
-					firsttime = 0;
-					pos_array_pos++;
-					/*
-					 * Check array index 
-					 */
-					if (pos_array_pos >= 4096) {
-						SDL_SetError("Exceeded maximum number of polygon points.");
-						pos_array_pos--;
-					}
-
-					/*
-					 * Store point in array 
-	 				 */
-					pos_array[pos_array_pos].x = target_x + 1;
-					pos_array[pos_array_pos].y = target_y;
-
-					/*
-					 * Mark the four poles as visited 
-					 */
-					if (*TL)
-						*TL = 0x99;
-					if (*BL)
-						*BL = 0x99;
-					if (*TR)
-						*TR = 0x99;
-					if (*BR)
-						*BR = 0x99;
-			
-					newtargetcount = 0;
-					if ((*TL || *TR) && (*TL != *TR)) {
-						newtargetcount++;
-						newtarget[newtargetcount].x = 0;
-						newtarget[newtargetcount].y = -1;
-					}
-
-					if ((*TR || *BR) && (*TR != *BR)) {
-						newtargetcount++;
-						newtarget[newtargetcount].x = 1;
-						newtarget[newtargetcount].y = 0;
-					}
-
-					if ((*BL || *BR) && (*BL != *BR)) {
-						newtargetcount++;
-						newtarget[newtargetcount].x = 0;
-	 					newtarget[newtargetcount].y = 1;
-					}
-
-					if ((*TL || *BL) && (*TL != *BL)) {
-						newtargetcount++;
-						newtarget[newtargetcount].x = -1;
-						newtarget[newtargetcount].y = 0;
-					}
-				
-					switch (newtargetcount) {
-						case 1:
-							SDL_SetError("Cropping error - Newtargetcount=1.");
-							return (-1);
-							break;
-
-						case 2:
-							if ((target_x + newtarget[1].x != lasttarget_x) || (target_y + newtarget[1].y != lasttarget_y)) {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								target_x = target_x + newtarget[1].x;
-								target_y = target_y + newtarget[1].y;
-							}
-							else {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								target_x = target_x + newtarget[2].x;
-								target_y = target_y + newtarget[2].y;
-							}
-							break;
-
-						case 3:
-							SDL_SetError("Cropping error - Newtargetcount=3.");
-							return (-1);
-							break;
-
-						case 4:
-							if (lasttarget_x > target_x) {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								if (*TR != 0x00)
-									target_y--;
-								else
-									target_y++;
-							}
-							else if (lasttarget_y > target_y) {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								if (*BL != 0x00)
-									target_x--;
-								else
-									target_x++;
-							}
-							else if (lasttarget_x < target_x) {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								if (*TL != 0x00)
-									target_y--;
-								else
-									target_y++;
-							}
-							else if (lasttarget_y < target_y) {
-								lasttarget_x = target_x;
-								lasttarget_y = target_y;
-								if (*TL != 0x00)
-									target_x--;
-								else
-									target_x++;
-							}
-							else {
-								SDL_SetError("Cropping error - no possible targets on newtargetcount=4.");
-								return (-1);
-							}
-							break;
-
-						default:
-							SDL_SetError("Cropping error - Newtargetcount invalid.");
-							return (-1);
-							break;
-					}
-
-					if (target_x > lasttarget_x)
-						TL = (Uint8 *) (TL + 1);
-					else if (target_x < lasttarget_x)
-						TL = (Uint8 *) (TL - 1);
-					else if (target_y > lasttarget_y)
-						TL = (Uint8 *) (TL + width);
-					else if (target_y < lasttarget_y)
-						TL = (Uint8 *) (TL - width);
-					else {
-						SDL_SetError("Cropping error - no new target.");
-						return (-1);
-					}
-
-					BL = (Uint8 *) (TL + width);
-					TR = (Uint8 *) (TL + 1);
-					BR = (Uint8 *) (BL + 1);
-				}			// End of while loop
-			
-				/*
-				 * Apply the mask to the cropping region 
-				 */
-				if (pos_array_pos >= 4) {
-					TempRegion = CreatePolygonRgn(&(pos_array[1]), pos_array_pos, WINDING);
-					if (TempRegion == NULL) {
-						SDL_SetError("Cropping error - unable to create polygon.");
-						return (-1);
-					}
-
-	  				/*
-	  				 * Add current region to final mask region 
-	  				 */
-	  				if (inside)
-	  					CombineRgn(MaskRegion, MaskRegion, TempRegion, RGN_DIFF);
-					else
-						CombineRgn(MaskRegion, MaskRegion, TempRegion, RGN_OR);
-
-					/*
-					 * Remove temporary region 
-					 */
-					DeleteObject(TempRegion);
-				}
-
-				/*
-				 * Switch sides 
-				 */
-				inside = !inside;
-			}
-			else if ((*curpos) && (!*curpos2))
-				inside = !inside;
-      			else if ((!*curpos) && (*curpos2))
-				inside = !inside;
-			
-			curpos++;
-			curpos2++;
-		}
-		curpos = (Uint8 *) (curpos + 2 * enlarge_mask - 1);
-		curpos2 = (Uint8 *) (curpos + 1);
-	}
+	SDL_TraverseShapeTree(data->mask_tree,&CombineRectRegions,&mask_region);
 	
 	/*
 	 * Set the new region mask for the window 
 	 */
-	SetWindowRgn((SDL_WindowData*)(shaper->window->driverdata)->hwnd, MaskRegion, TRUE);
-
-	/*
-	 * Return value 
-	 */
-	return (0);
+	SetWindowRgn((SDL_WindowData*)(shaper->window->driverdata)->hwnd, mask_region, TRUE);
+	
+	return 0;
 }
 
 int Win32_ResizeWindowShape(SDL_Window *window) {
 	SDL_ShapeData* data = window->shaper->driverdata;
 	assert(data != NULL);
 	
-	unsigned int buffersize = window->w * window->h;
-	if(data->buffersize != buffersize || data->shapebuffer == NULL) {
-		data->buffersize = buffersize;
-		if(data->shapebuffer != NULL)
-			free(data->shapebuffer);
-		data->shapebuffer = malloc(data->buffersize);
-		if(data->shapebuffer == NULL) {
-			SDL_SetError("Could not allocate memory for shaped-window bitmap.");
-			return -1;
-		}
-	}
-	else
-		memset(data->shapebuffer,0,data->buffersize);
+	if(data->mask_tree != NULL)
+		SDL_FreeShapeTree(&data->mask_tree);
 	
 	window->shaper->usershownflag |= window->flags & SDL_WINDOW_SHOWN;
 	
--- a/src/video/win32/SDL_win32shape.h	Mon Jul 26 21:49:32 2010 -0400
+++ b/src/video/win32/SDL_win32shape.h	Wed Jul 28 23:35:24 2010 -0400
@@ -28,13 +28,12 @@
 #include "SDL_video.h"
 #include "SDL_shape.h"
 #include "../SDL_sysvideo.h"
+#include "../SDL_shape_internals.h"
 
 typedef struct {
-	void* shapebuffer;
-	Uint32 buffersize;
+	SDL_ShapeTree *mask_tree;
 } SDL_ShapeData;
 
-extern SDL_Window* Win32_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags);
 extern SDL_WindowShaper* Win32_CreateShaper(SDL_Window * window);
 extern int Win32_SetWindowShape(SDL_WindowShaper *shaper,SDL_Surface *shape,SDL_WindowShapeMode *shapeMode);
 extern int Win32_ResizeWindowShape(SDL_Window *window);