1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../SDL_internal.h"
22 
23 #include "SDL.h"
24 #include "SDL_assert.h"
25 #include "SDL_video.h"
26 #include "SDL_sysvideo.h"
27 #include "SDL_pixels.h"
28 #include "SDL_surface.h"
29 #include "SDL_shape.h"
30 #include "SDL_shape_internals.h"
31 
32 SDL_Window*
SDL_CreateShapedWindow(const char * title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags)33 SDL_CreateShapedWindow(const char *title,unsigned int x,unsigned int y,unsigned int w,unsigned int h,Uint32 flags)
34 {
35     SDL_Window *result = NULL;
36     result = SDL_CreateWindow(title,-1000,-1000,w,h,(flags | SDL_WINDOW_BORDERLESS) & (~SDL_WINDOW_FULLSCREEN) & (~SDL_WINDOW_RESIZABLE) /* & (~SDL_WINDOW_SHOWN) */);
37     if(result != NULL) {
38         result->shaper = SDL_GetVideoDevice()->shape_driver.CreateShaper(result);
39         if(result->shaper != NULL) {
40             result->shaper->userx = x;
41             result->shaper->usery = y;
42             result->shaper->mode.mode = ShapeModeDefault;
43             result->shaper->mode.parameters.binarizationCutoff = 1;
44             result->shaper->hasshape = SDL_FALSE;
45             return result;
46         }
47         else {
48             SDL_DestroyWindow(result);
49             return NULL;
50         }
51     }
52     else
53         return NULL;
54 }
55 
56 SDL_bool
SDL_IsShapedWindow(const SDL_Window * window)57 SDL_IsShapedWindow(const SDL_Window *window)
58 {
59     if(window == NULL)
60         return SDL_FALSE;
61     else
62         return (SDL_bool)(window->shaper != NULL);
63 }
64 
65 /* REQUIRES that bitmap point to a w-by-h bitmap with ppb pixels-per-byte. */
66 void
SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface * shape,Uint8 * bitmap,Uint8 ppb)67 SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface *shape,Uint8* bitmap,Uint8 ppb)
68 {
69     int x = 0;
70     int y = 0;
71     Uint8 r = 0,g = 0,b = 0,alpha = 0;
72     Uint8* pixel = NULL;
73     Uint32 bitmap_pixel,pixel_value = 0,mask_value = 0;
74     SDL_Color key;
75     if(SDL_MUSTLOCK(shape))
76         SDL_LockSurface(shape);
77     for(y = 0;y<shape->h;y++) {
78         for(x=0;x<shape->w;x++) {
79             alpha = 0;
80             pixel_value = 0;
81             pixel = (Uint8 *)(shape->pixels) + (y*shape->pitch) + (x*shape->format->BytesPerPixel);
82             switch(shape->format->BytesPerPixel) {
83                 case(1):
84                     pixel_value = *(Uint8*)pixel;
85                     break;
86                 case(2):
87                     pixel_value = *(Uint16*)pixel;
88                     break;
89                 case(3):
90                     pixel_value = *(Uint32*)pixel & (~shape->format->Amask);
91                     break;
92                 case(4):
93                     pixel_value = *(Uint32*)pixel;
94                     break;
95             }
96             SDL_GetRGBA(pixel_value,shape->format,&r,&g,&b,&alpha);
97             bitmap_pixel = y*shape->w + x;
98             switch(mode.mode) {
99                 case(ShapeModeDefault):
100                     mask_value = (alpha >= 1 ? 1 : 0);
101                     break;
102                 case(ShapeModeBinarizeAlpha):
103                     mask_value = (alpha >= mode.parameters.binarizationCutoff ? 1 : 0);
104                     break;
105                 case(ShapeModeReverseBinarizeAlpha):
106                     mask_value = (alpha <= mode.parameters.binarizationCutoff ? 1 : 0);
107                     break;
108                 case(ShapeModeColorKey):
109                     key = mode.parameters.colorKey;
110                     mask_value = ((key.r != r || key.g != g || key.b != b) ? 1 : 0);
111                     break;
112             }
113             bitmap[bitmap_pixel / ppb] |= mask_value << (7 - ((ppb - 1) - (bitmap_pixel % ppb)));
114         }
115     }
116     if(SDL_MUSTLOCK(shape))
117         SDL_UnlockSurface(shape);
118 }
119 
120 static SDL_ShapeTree*
RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface * mask,SDL_Rect dimensions)121 RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* mask,SDL_Rect dimensions) {
122     int x = 0,y = 0;
123     Uint8* pixel = NULL;
124     Uint32 pixel_value = 0;
125     Uint8 r = 0,g = 0,b = 0,a = 0;
126     SDL_bool pixel_opaque = SDL_FALSE;
127     int last_opaque = -1;
128     SDL_Color key;
129     SDL_ShapeTree* result = (SDL_ShapeTree*)SDL_malloc(sizeof(SDL_ShapeTree));
130     SDL_Rect next = {0,0,0,0};
131 
132     for(y=dimensions.y;y<dimensions.y + dimensions.h;y++) {
133         for(x=dimensions.x;x<dimensions.x + dimensions.w;x++) {
134             pixel_value = 0;
135             pixel = (Uint8 *)(mask->pixels) + (y*mask->pitch) + (x*mask->format->BytesPerPixel);
136             switch(mask->format->BytesPerPixel) {
137                 case(1):
138                     pixel_value = *(Uint8*)pixel;
139                     break;
140                 case(2):
141                     pixel_value = *(Uint16*)pixel;
142                     break;
143                 case(3):
144                     pixel_value = *(Uint32*)pixel & (~mask->format->Amask);
145                     break;
146                 case(4):
147                     pixel_value = *(Uint32*)pixel;
148                     break;
149             }
150             SDL_GetRGBA(pixel_value,mask->format,&r,&g,&b,&a);
151             switch(mode.mode) {
152                 case(ShapeModeDefault):
153                     pixel_opaque = (a >= 1 ? SDL_TRUE : SDL_FALSE);
154                     break;
155                 case(ShapeModeBinarizeAlpha):
156                     pixel_opaque = (a >= mode.parameters.binarizationCutoff ? SDL_TRUE : SDL_FALSE);
157                     break;
158                 case(ShapeModeReverseBinarizeAlpha):
159                     pixel_opaque = (a <= mode.parameters.binarizationCutoff ? SDL_TRUE : SDL_FALSE);
160                     break;
161                 case(ShapeModeColorKey):
162                     key = mode.parameters.colorKey;
163                     pixel_opaque = ((key.r != r || key.g != g || key.b != b) ? SDL_TRUE : SDL_FALSE);
164                     break;
165             }
166             if(last_opaque == -1)
167                 last_opaque = pixel_opaque;
168             if(last_opaque != pixel_opaque) {
169                 const int halfwidth = dimensions.w / 2;
170                 const int halfheight = dimensions.h / 2;
171 
172                 result->kind = QuadShape;
173 
174                 next.x = dimensions.x;
175                 next.y = dimensions.y;
176                 next.w = halfwidth;
177                 next.h = halfheight;
178                 result->data.children.upleft = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
179 
180                 next.x = dimensions.x + halfwidth;
181                 next.w = dimensions.w - halfwidth;
182                 result->data.children.upright = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
183 
184                 next.x = dimensions.x;
185                 next.w = halfwidth;
186                 next.y = dimensions.y + halfheight;
187                 next.h = dimensions.h - halfheight;
188                 result->data.children.downleft = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
189 
190                 next.x = dimensions.x + halfwidth;
191                 next.w = dimensions.w - halfwidth;
192                 result->data.children.downright = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
193 
194                 return result;
195             }
196         }
197     }
198 
199 
200     /* If we never recursed, all the pixels in this quadrant have the same "value". */
201     result->kind = (last_opaque == SDL_TRUE ? OpaqueShape : TransparentShape);
202     result->data.shape = dimensions;
203     return result;
204 }
205 
206 SDL_ShapeTree*
SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface * shape)207 SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* shape)
208 {
209     SDL_Rect dimensions = {0,0,shape->w,shape->h};
210     SDL_ShapeTree* result = NULL;
211     if(SDL_MUSTLOCK(shape))
212         SDL_LockSurface(shape);
213     result = RecursivelyCalculateShapeTree(mode,shape,dimensions);
214     if(SDL_MUSTLOCK(shape))
215         SDL_UnlockSurface(shape);
216     return result;
217 }
218 
219 void
SDL_TraverseShapeTree(SDL_ShapeTree * tree,SDL_TraversalFunction function,void * closure)220 SDL_TraverseShapeTree(SDL_ShapeTree *tree,SDL_TraversalFunction function,void* closure)
221 {
222     SDL_assert(tree != NULL);
223     if(tree->kind == QuadShape) {
224         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.upleft,function,closure);
225         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.upright,function,closure);
226         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.downleft,function,closure);
227         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.downright,function,closure);
228     }
229     else
230         function(tree,closure);
231 }
232 
233 void
SDL_FreeShapeTree(SDL_ShapeTree ** shape_tree)234 SDL_FreeShapeTree(SDL_ShapeTree** shape_tree)
235 {
236     if((*shape_tree)->kind == QuadShape) {
237         SDL_FreeShapeTree((SDL_ShapeTree **)&(*shape_tree)->data.children.upleft);
238         SDL_FreeShapeTree((SDL_ShapeTree **)&(*shape_tree)->data.children.upright);
239         SDL_FreeShapeTree((SDL_ShapeTree **)&(*shape_tree)->data.children.downleft);
240         SDL_FreeShapeTree((SDL_ShapeTree **)&(*shape_tree)->data.children.downright);
241     }
242     SDL_free(*shape_tree);
243     *shape_tree = NULL;
244 }
245 
246 int
SDL_SetWindowShape(SDL_Window * window,SDL_Surface * shape,SDL_WindowShapeMode * shape_mode)247 SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape,SDL_WindowShapeMode *shape_mode)
248 {
249     int result;
250     if(window == NULL || !SDL_IsShapedWindow(window))
251         /* The window given was not a shapeable window. */
252         return SDL_NONSHAPEABLE_WINDOW;
253     if(shape == NULL)
254         /* Invalid shape argument. */
255         return SDL_INVALID_SHAPE_ARGUMENT;
256 
257     if(shape_mode != NULL)
258         window->shaper->mode = *shape_mode;
259     result = SDL_GetVideoDevice()->shape_driver.SetWindowShape(window->shaper,shape,shape_mode);
260     window->shaper->hasshape = SDL_TRUE;
261     if(window->shaper->userx != 0 && window->shaper->usery != 0) {
262         SDL_SetWindowPosition(window,window->shaper->userx,window->shaper->usery);
263         window->shaper->userx = 0;
264         window->shaper->usery = 0;
265     }
266     return result;
267 }
268 
269 static SDL_bool
SDL_WindowHasAShape(SDL_Window * window)270 SDL_WindowHasAShape(SDL_Window *window)
271 {
272     if (window == NULL || !SDL_IsShapedWindow(window))
273         return SDL_FALSE;
274     return window->shaper->hasshape;
275 }
276 
277 int
SDL_GetShapedWindowMode(SDL_Window * window,SDL_WindowShapeMode * shape_mode)278 SDL_GetShapedWindowMode(SDL_Window *window,SDL_WindowShapeMode *shape_mode)
279 {
280     if(window != NULL && SDL_IsShapedWindow(window)) {
281         if(shape_mode == NULL) {
282             if(SDL_WindowHasAShape(window))
283                 /* The window given has a shape. */
284                 return 0;
285             else
286                 /* The window given is shapeable but lacks a shape. */
287                 return SDL_WINDOW_LACKS_SHAPE;
288         }
289         else {
290             *shape_mode = window->shaper->mode;
291             return 0;
292         }
293     }
294     else
295         /* The window given is not a valid shapeable window. */
296         return SDL_NONSHAPEABLE_WINDOW;
297 }
298