src/video/SDL_shape.c
author Eli Gottlieb <eligottlieb@gmail.com>
Wed, 28 Jul 2010 23:35:24 -0400
changeset 4813 5b4c7d7d8953
parent 4810 7a602fd2121f
child 4815 93402b9dd20c
permissions -rw-r--r--
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.
eligottlieb@4765
     1
/*
eligottlieb@4765
     2
    SDL - Simple DirectMedia Layer
eligottlieb@4765
     3
    Copyright (C) 2010 Eli Gottlieb
eligottlieb@4765
     4
eligottlieb@4765
     5
    This library is free software; you can redistribute it and/or
eligottlieb@4765
     6
    modify it under the terms of the GNU Lesser General Public
eligottlieb@4765
     7
    License as published by the Free Software Foundation; either
eligottlieb@4765
     8
    version 2.1 of the License, or (at your option) any later version.
eligottlieb@4765
     9
eligottlieb@4765
    10
    This library is distributed in the hope that it will be useful,
eligottlieb@4765
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
eligottlieb@4765
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
eligottlieb@4765
    13
    Lesser General Public License for more details.
eligottlieb@4765
    14
eligottlieb@4765
    15
    You should have received a copy of the GNU Lesser General Public
eligottlieb@4765
    16
    License along with this library; if not, write to the Free Software
eligottlieb@4765
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
eligottlieb@4765
    18
eligottlieb@4765
    19
    Eli Gottlieb
eligottlieb@4765
    20
    eligottlieb@gmail.com
eligottlieb@4765
    21
*/
eligottlieb@4769
    22
#include "SDL_config.h"
eligottlieb@4776
    23
eligottlieb@4769
    24
#include "SDL.h"
eligottlieb@4769
    25
#include "SDL_video.h"
eligottlieb@4769
    26
#include "SDL_sysvideo.h"
eligottlieb@4782
    27
#include "SDL_pixels.h"
eligottlieb@4782
    28
#include "SDL_surface.h"
eligottlieb@4765
    29
#include "SDL_shape.h"
eligottlieb@4813
    30
#include "SDL_shape_internals.h"
eligottlieb@4766
    31
eligottlieb@4769
    32
SDL_Window* SDL_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags) {
eligottlieb@4810
    33
	SDL_Window *result = SDL_CreateWindow(title,x,y,w,h,SDL_WINDOW_BORDERLESS | flags & !SDL_WINDOW_FULLSCREEN & !SDL_WINDOW_SHOWN);
eligottlieb@4796
    34
	if(result != NULL) {
eligottlieb@4796
    35
		result->shaper = result->display->device->shape_driver.CreateShaper(result);
eligottlieb@4796
    36
		if(result->shaper != NULL) {
eligottlieb@4796
    37
			result->shaper->usershownflag = flags & SDL_WINDOW_SHOWN;
eligottlieb@4807
    38
			result->shaper->mode.mode = ShapeModeDefault;
eligottlieb@4807
    39
			result->shaper->mode.parameters.binarizationCutoff = 1;
eligottlieb@4796
    40
			result->shaper->hasshape = SDL_FALSE;
eligottlieb@4796
    41
			return result;
eligottlieb@4796
    42
		}
eligottlieb@4796
    43
		else {
eligottlieb@4796
    44
			SDL_DestroyWindow(result);
eligottlieb@4796
    45
			return NULL;
eligottlieb@4796
    46
		}
eligottlieb@4796
    47
	}
eligottlieb@4796
    48
	else
eligottlieb@4796
    49
		return NULL;
eligottlieb@4766
    50
}
eligottlieb@4766
    51
eligottlieb@4781
    52
SDL_bool SDL_IsShapedWindow(const SDL_Window *window) {
eligottlieb@4782
    53
	if(window == NULL)
eligottlieb@4782
    54
		return SDL_FALSE;
eligottlieb@4782
    55
	else
eligottlieb@4789
    56
		return (SDL_bool)(window->shaper != NULL);
eligottlieb@4782
    57
}
eligottlieb@4782
    58
eligottlieb@4813
    59
/* REQUIRES that bitmap point to a w-by-h bitmap with ppb pixels-per-byte. */
eligottlieb@4807
    60
void SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface *shape,Uint8* bitmap,Uint8 ppb,Uint8 value) {
eligottlieb@4787
    61
	int x = 0;
eligottlieb@4787
    62
	int y = 0;
eligottlieb@4795
    63
	Uint8 r = 0,g = 0,b = 0,alpha = 0;
eligottlieb@4802
    64
	Uint8* pixel = NULL;
eligottlieb@4802
    65
	Uint32 bitmap_pixel,pixel_value = 0;
eligottlieb@4807
    66
	SDL_Color key;
eligottlieb@4782
    67
	if(SDL_MUSTLOCK(shape))
eligottlieb@4782
    68
		SDL_LockSurface(shape);
eligottlieb@4799
    69
	pixel = (Uint8*)shape->pixels;
eligottlieb@4799
    70
	for(y = 0;y<shape->h;y++) {
eligottlieb@4799
    71
		for(x=0;x<shape->w;x++) {
eligottlieb@4787
    72
			alpha = 0;
eligottlieb@4802
    73
			pixel_value = 0;
eligottlieb@4806
    74
			pixel = (Uint8 *)(shape->pixels) + (y*shape->pitch) + (x*shape->format->BytesPerPixel);
eligottlieb@4802
    75
			switch(shape->format->BytesPerPixel) {
eligottlieb@4802
    76
				case(1):
eligottlieb@4802
    77
					pixel_value = *(Uint8*)pixel;
eligottlieb@4802
    78
					break;
eligottlieb@4802
    79
				case(2):
eligottlieb@4802
    80
					pixel_value = *(Uint16*)pixel;
eligottlieb@4802
    81
					break;
eligottlieb@4802
    82
				case(4):
eligottlieb@4802
    83
					pixel_value = *(Uint32*)pixel;
eligottlieb@4802
    84
					break;
eligottlieb@4802
    85
			}
eligottlieb@4802
    86
			SDL_GetRGBA(pixel_value,shape->format,&r,&g,&b,&alpha);
eligottlieb@4796
    87
			bitmap_pixel = y*shape->w + x;
eligottlieb@4807
    88
			switch(mode.mode) {
eligottlieb@4807
    89
				case(ShapeModeDefault):
eligottlieb@4807
    90
					bitmap[bitmap_pixel / ppb] |= (alpha >= 1 ? value : 0) << ((ppb - 1) - (bitmap_pixel % ppb));
eligottlieb@4807
    91
					break;
eligottlieb@4807
    92
				case(ShapeModeBinarizeAlpha):
eligottlieb@4807
    93
					bitmap[bitmap_pixel / ppb] |= (alpha >= mode.parameters.binarizationCutoff ? value : 0) << ((ppb - 1) - (bitmap_pixel % ppb));
eligottlieb@4807
    94
					break;
eligottlieb@4807
    95
				case(ShapeModeReverseBinarizeAlpha):
eligottlieb@4807
    96
					bitmap[bitmap_pixel / ppb] |= (alpha <= mode.parameters.binarizationCutoff ? value : 0) << ((ppb - 1) - (bitmap_pixel % ppb));
eligottlieb@4807
    97
					break;
eligottlieb@4807
    98
				case(ShapeModeColorKey):
eligottlieb@4807
    99
					key = mode.parameters.colorKey;
eligottlieb@4808
   100
					bitmap[bitmap_pixel / ppb] |= ((key.r == r && key.g == g && key.b == b) ? value : 0) << ((ppb - 1) - (bitmap_pixel % ppb));
eligottlieb@4807
   101
					break;
eligottlieb@4807
   102
			}
eligottlieb@4782
   103
		}
eligottlieb@4799
   104
	}
eligottlieb@4782
   105
	if(SDL_MUSTLOCK(shape))
eligottlieb@4782
   106
		SDL_UnlockSurface(shape);
eligottlieb@4769
   107
}
eligottlieb@4769
   108
eligottlieb@4813
   109
SDL_ShapeTree* RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* mask,SDL_bool invert,SDL_Rect dimensions) {
eligottlieb@4813
   110
	int x = 0,y = 0;
eligottlieb@4813
   111
	Uint8* pixel = NULL;
eligottlieb@4813
   112
	Uint32 pixel_value = 0;
eligottlieb@4813
   113
	Uint8 r = 0,g = 0,b = 0,a = 0;
eligottlieb@4813
   114
	SDL_bool pixel_transparent = SDL_FALSE;
eligottlieb@4813
   115
	int last_transparent = -1;
eligottlieb@4813
   116
	SDL_Color key;
eligottlieb@4813
   117
	SDL_ShapeTree* result = malloc(sizeof(SDL_ShapeTree));
eligottlieb@4813
   118
	SDL_Rect next = {0,0,0,0};
eligottlieb@4813
   119
	for(y=dimensions.y;y<dimensions.h;y++)
eligottlieb@4813
   120
		for(x=dimensions.x;x<dimensions.w;x++) {
eligottlieb@4813
   121
			pixel_value = 0;
eligottlieb@4813
   122
			pixel = (Uint8 *)(mask->pixels) + (y*mask->pitch) + (x*mask->format->BytesPerPixel);
eligottlieb@4813
   123
			switch(mask->format->BytesPerPixel) {
eligottlieb@4813
   124
				case(1):
eligottlieb@4813
   125
					pixel_value = *(Uint8*)pixel;
eligottlieb@4813
   126
					break;
eligottlieb@4813
   127
				case(2):
eligottlieb@4813
   128
					pixel_value = *(Uint16*)pixel;
eligottlieb@4813
   129
					break;
eligottlieb@4813
   130
				case(4):
eligottlieb@4813
   131
					pixel_value = *(Uint32*)pixel;
eligottlieb@4813
   132
					break;
eligottlieb@4813
   133
			}
eligottlieb@4813
   134
			SDL_GetRGBA(pixel_value,mask->format,&r,&g,&b,&a);
eligottlieb@4813
   135
			switch(mode.mode) {
eligottlieb@4813
   136
				case(ShapeModeDefault):
eligottlieb@4813
   137
					pixel_transparent = (a >= 1 ? !invert : invert);
eligottlieb@4813
   138
					break;
eligottlieb@4813
   139
				case(ShapeModeBinarizeAlpha):
eligottlieb@4813
   140
					pixel_transparent = (a >= mode.parameters.binarizationCutoff ? !invert : invert);
eligottlieb@4813
   141
					break;
eligottlieb@4813
   142
				case(ShapeModeReverseBinarizeAlpha):
eligottlieb@4813
   143
					pixel_transparent = (a <= mode.parameters.binarizationCutoff ? !invert : invert);
eligottlieb@4813
   144
					break;
eligottlieb@4813
   145
				case(ShapeModeColorKey):
eligottlieb@4813
   146
					key = mode.parameters.colorKey;
eligottlieb@4813
   147
					pixel_transparent = ((key.r == r && key.g == g && key.b == b) ? !invert : invert);
eligottlieb@4813
   148
					break;
eligottlieb@4813
   149
			}
eligottlieb@4813
   150
			if(last_transparent == -1) {
eligottlieb@4813
   151
				last_transparent = pixel_transparent;
eligottlieb@4813
   152
				break;
eligottlieb@4813
   153
			}
eligottlieb@4813
   154
			if(last_transparent != pixel_transparent) {
eligottlieb@4813
   155
				result->kind = QuadShape;
eligottlieb@4813
   156
				//These will stay the same.
eligottlieb@4813
   157
				next.w = dimensions.w / 2;
eligottlieb@4813
   158
				next.h = dimensions.h / 2;
eligottlieb@4813
   159
				//These will change from recursion to recursion.
eligottlieb@4813
   160
				next.x = dimensions.x;
eligottlieb@4813
   161
				next.y = dimensions.y;
eligottlieb@4813
   162
				result->data.children.upleft = RecursivelyCalculateShapeTree(mode,mask,invert,next);
eligottlieb@4813
   163
				next.x = dimensions.w / 2 + 1;
eligottlieb@4813
   164
				//Unneeded: next.y = dimensions.y;
eligottlieb@4813
   165
				result->data.children.upright = RecursivelyCalculateShapeTree(mode,mask,invert,next);
eligottlieb@4813
   166
				next.x = dimensions.x;
eligottlieb@4813
   167
				next.y = dimensions.h / 2 + 1;
eligottlieb@4813
   168
				result->data.children.downleft = RecursivelyCalculateShapeTree(mode,mask,invert,next);
eligottlieb@4813
   169
				next.x = dimensions.w / 2 + 1;
eligottlieb@4813
   170
				//Unneeded: next.y = dimensions.h / 2 + 1;
eligottlieb@4813
   171
				result->data.children.downright = RecursivelyCalculateShapeTree(mode,mask,invert,next);
eligottlieb@4813
   172
				return result;
eligottlieb@4813
   173
			}
eligottlieb@4813
   174
		}
eligottlieb@4813
   175
	//If we never recursed, all the pixels in this quadrant have the same "value".
eligottlieb@4813
   176
	result->kind = (last_transparent == SDL_FALSE ? OpaqueShape : TransparentShape);
eligottlieb@4813
   177
	result->data.shape = dimensions;
eligottlieb@4813
   178
	return result;
eligottlieb@4813
   179
}
eligottlieb@4813
   180
eligottlieb@4813
   181
SDL_ShapeTree* SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* shape,SDL_bool invert) {
eligottlieb@4813
   182
	SDL_Rect dimensions = {0,0,shape->w,shape->h};
eligottlieb@4813
   183
	SDL_ShapeTree* result = NULL;
eligottlieb@4813
   184
	if(SDL_MUSTLOCK(shape))
eligottlieb@4813
   185
		SDL_LockSurface(shape);
eligottlieb@4813
   186
	result = RecursivelyCalculateShapeTree(mode,shape,invert,dimensions);
eligottlieb@4813
   187
	if(SDL_MUSTLOCK(shape))
eligottlieb@4813
   188
		SDL_UnlockSurface(shape);
eligottlieb@4813
   189
	return result;
eligottlieb@4813
   190
}
eligottlieb@4813
   191
eligottlieb@4813
   192
void SDL_TraverseShapeTree(SDL_ShapeTree *tree,void(*function)(SDL_ShapeTree*,void*),void* closure) {
eligottlieb@4813
   193
	if(tree->kind == QuadShape) {
eligottlieb@4813
   194
		SDL_TraverseShapeTree(tree->data.children.upleft,function,closure);
eligottlieb@4813
   195
		SDL_TraverseShapeTree(tree->data.children.upright,function,closure);
eligottlieb@4813
   196
		SDL_TraverseShapeTree(tree->data.children.downleft,function,closure);
eligottlieb@4813
   197
		SDL_TraverseShapeTree(tree->data.children.downright,function,closure);
eligottlieb@4813
   198
	}
eligottlieb@4813
   199
	else
eligottlieb@4813
   200
		function(tree,closure);
eligottlieb@4813
   201
}
eligottlieb@4813
   202
eligottlieb@4813
   203
void SDL_FreeShapeTree(SDL_ShapeTree** shapeTree) {
eligottlieb@4813
   204
	if((*shapeTree)->kind == QuadShape) {
eligottlieb@4813
   205
		SDL_FreeShapeTree(&(*shapeTree)->data.children.upleft);
eligottlieb@4813
   206
		SDL_FreeShapeTree(&(*shapeTree)->data.children.upright);
eligottlieb@4813
   207
		SDL_FreeShapeTree(&(*shapeTree)->data.children.downleft);
eligottlieb@4813
   208
		SDL_FreeShapeTree(&(*shapeTree)->data.children.downright);
eligottlieb@4813
   209
	}
eligottlieb@4813
   210
	free(*shapeTree);
eligottlieb@4813
   211
	*shapeTree = NULL;
eligottlieb@4813
   212
}
eligottlieb@4813
   213
eligottlieb@4781
   214
int SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape,SDL_WindowShapeMode *shapeMode) {
eligottlieb@4787
   215
	int result;
eligottlieb@4786
   216
	if(window == NULL || !SDL_IsShapedWindow(window))
eligottlieb@4782
   217
		//The window given was not a shapeable window.
eligottlieb@4801
   218
		return SDL_NONSHAPEABLE_WINDOW;
eligottlieb@4780
   219
	if(shape == NULL)
eligottlieb@4782
   220
		//Invalid shape argument.
eligottlieb@4801
   221
		return SDL_INVALID_SHAPE_ARGUMENT;
eligottlieb@4782
   222
	
eligottlieb@4807
   223
	if(shapeMode != NULL)
eligottlieb@4807
   224
		window->shaper->mode = *shapeMode;
eligottlieb@4802
   225
	//TODO: Platform-specific implementations of SetWindowShape.  X11 is finished.  Win32 is finished.  Debugging is in progress on both.
eligottlieb@4787
   226
	result = window->display->device->shape_driver.SetWindowShape(window->shaper,shape,shapeMode);
eligottlieb@4782
   227
	window->shaper->hasshape = SDL_TRUE;
eligottlieb@4787
   228
	if((window->shaper->usershownflag & SDL_WINDOW_SHOWN) == SDL_WINDOW_SHOWN) {
eligottlieb@4782
   229
		SDL_ShowWindow(window);
eligottlieb@4782
   230
		window->shaper->usershownflag &= !SDL_WINDOW_SHOWN;
eligottlieb@4782
   231
	}
eligottlieb@4782
   232
	return result;
eligottlieb@4782
   233
}
eligottlieb@4782
   234
eligottlieb@4782
   235
SDL_bool SDL_WindowHasAShape(SDL_Window *window) {
eligottlieb@4787
   236
	if (window == NULL && !SDL_IsShapedWindow(window))
eligottlieb@4787
   237
		return SDL_FALSE;
eligottlieb@4782
   238
	return window->shaper->hasshape;
eligottlieb@4778
   239
}
eligottlieb@4778
   240
eligottlieb@4781
   241
int SDL_GetShapedWindowMode(SDL_Window *window,SDL_WindowShapeMode *shapeMode) {
eligottlieb@4782
   242
	if(window != NULL && SDL_IsShapedWindow(window)) {
eligottlieb@4782
   243
		if(shapeMode == NULL) {
eligottlieb@4782
   244
			if(SDL_WindowHasAShape(window))
eligottlieb@4782
   245
				//The window given has a shape.
eligottlieb@4782
   246
				return 0;
eligottlieb@4782
   247
			else
eligottlieb@4782
   248
				//The window given is shapeable but lacks a shape.
eligottlieb@4801
   249
				return SDL_WINDOW_LACKS_SHAPE;
eligottlieb@4782
   250
		}
eligottlieb@4782
   251
		else {
eligottlieb@4807
   252
			*shapeMode = window->shaper->mode;
eligottlieb@4782
   253
			return 0;
eligottlieb@4782
   254
		}
eligottlieb@4782
   255
	}
eligottlieb@4782
   256
	else
eligottlieb@4782
   257
		//The window given is not a valid shapeable window.
eligottlieb@4801
   258
		return SDL_NONSHAPEABLE_WINDOW;
eligottlieb@4778
   259
}