1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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         if (SDL_GetVideoDevice()->shape_driver.CreateShaper == NULL) {
39             SDL_DestroyWindow(result);
40             return NULL;
41         }
42         result->shaper = SDL_GetVideoDevice()->shape_driver.CreateShaper(result);
43         if(result->shaper != NULL) {
44             result->shaper->userx = x;
45             result->shaper->usery = y;
46             result->shaper->mode.mode = ShapeModeDefault;
47             result->shaper->mode.parameters.binarizationCutoff = 1;
48             result->shaper->hasshape = SDL_FALSE;
49             return result;
50         }
51         else {
52             SDL_DestroyWindow(result);
53             return NULL;
54         }
55     }
56     else
57         return NULL;
58 }
59 
60 SDL_bool
SDL_IsShapedWindow(const SDL_Window * window)61 SDL_IsShapedWindow(const SDL_Window *window)
62 {
63     if(window == NULL)
64         return SDL_FALSE;
65     else
66         return (SDL_bool)(window->shaper != NULL);
67 }
68 
69 /* REQUIRES that bitmap point to a w-by-h bitmap with ppb pixels-per-byte. */
70 void
SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface * shape,Uint8 * bitmap,Uint8 ppb)71 SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode,SDL_Surface *shape,Uint8* bitmap,Uint8 ppb)
72 {
73     int x = 0;
74     int y = 0;
75     Uint8 r = 0,g = 0,b = 0,alpha = 0;
76     Uint8* pixel = NULL;
77     Uint32 pixel_value = 0,mask_value = 0;
78     int bytes_per_scanline = (shape->w + (ppb - 1)) / ppb;
79     Uint8 *bitmap_scanline;
80     SDL_Color key;
81     if(SDL_MUSTLOCK(shape))
82         SDL_LockSurface(shape);
83     for(y = 0;y<shape->h;y++) {
84         bitmap_scanline = bitmap + y * bytes_per_scanline;
85         for(x=0;x<shape->w;x++) {
86             alpha = 0;
87             pixel_value = 0;
88             pixel = (Uint8 *)(shape->pixels) + (y*shape->pitch) + (x*shape->format->BytesPerPixel);
89             switch(shape->format->BytesPerPixel) {
90                 case(1):
91                     pixel_value = *pixel;
92                     break;
93                 case(2):
94                     pixel_value = *(Uint16*)pixel;
95                     break;
96                 case(3):
97                     pixel_value = *(Uint32*)pixel & (~shape->format->Amask);
98                     break;
99                 case(4):
100                     pixel_value = *(Uint32*)pixel;
101                     break;
102             }
103             SDL_GetRGBA(pixel_value,shape->format,&r,&g,&b,&alpha);
104             switch(mode.mode) {
105                 case(ShapeModeDefault):
106                     mask_value = (alpha >= 1 ? 1 : 0);
107                     break;
108                 case(ShapeModeBinarizeAlpha):
109                     mask_value = (alpha >= mode.parameters.binarizationCutoff ? 1 : 0);
110                     break;
111                 case(ShapeModeReverseBinarizeAlpha):
112                     mask_value = (alpha <= mode.parameters.binarizationCutoff ? 1 : 0);
113                     break;
114                 case(ShapeModeColorKey):
115                     key = mode.parameters.colorKey;
116                     mask_value = ((key.r != r || key.g != g || key.b != b) ? 1 : 0);
117                     break;
118             }
119             bitmap_scanline[x / ppb] |= mask_value << (x % ppb);
120         }
121     }
122     if(SDL_MUSTLOCK(shape))
123         SDL_UnlockSurface(shape);
124 }
125 
126 static SDL_ShapeTree*
RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface * mask,SDL_Rect dimensions)127 RecursivelyCalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* mask,SDL_Rect dimensions) {
128     int x = 0,y = 0;
129     Uint8* pixel = NULL;
130     Uint32 pixel_value = 0;
131     Uint8 r = 0,g = 0,b = 0,a = 0;
132     SDL_bool pixel_opaque = SDL_FALSE;
133     int last_opaque = -1;
134     SDL_Color key;
135     SDL_ShapeTree* result = (SDL_ShapeTree*)SDL_malloc(sizeof(SDL_ShapeTree));
136     SDL_Rect next = {0,0,0,0};
137 
138     for(y=dimensions.y;y<dimensions.y + dimensions.h;y++) {
139         for(x=dimensions.x;x<dimensions.x + dimensions.w;x++) {
140             pixel_value = 0;
141             pixel = (Uint8 *)(mask->pixels) + (y*mask->pitch) + (x*mask->format->BytesPerPixel);
142             switch(mask->format->BytesPerPixel) {
143                 case(1):
144                     pixel_value = *pixel;
145                     break;
146                 case(2):
147                     pixel_value = *(Uint16*)pixel;
148                     break;
149                 case(3):
150                     pixel_value = *(Uint32*)pixel & (~mask->format->Amask);
151                     break;
152                 case(4):
153                     pixel_value = *(Uint32*)pixel;
154                     break;
155             }
156             SDL_GetRGBA(pixel_value,mask->format,&r,&g,&b,&a);
157             switch(mode.mode) {
158                 case(ShapeModeDefault):
159                     pixel_opaque = (a >= 1 ? SDL_TRUE : SDL_FALSE);
160                     break;
161                 case(ShapeModeBinarizeAlpha):
162                     pixel_opaque = (a >= mode.parameters.binarizationCutoff ? SDL_TRUE : SDL_FALSE);
163                     break;
164                 case(ShapeModeReverseBinarizeAlpha):
165                     pixel_opaque = (a <= mode.parameters.binarizationCutoff ? SDL_TRUE : SDL_FALSE);
166                     break;
167                 case(ShapeModeColorKey):
168                     key = mode.parameters.colorKey;
169                     pixel_opaque = ((key.r != r || key.g != g || key.b != b) ? SDL_TRUE : SDL_FALSE);
170                     break;
171             }
172             if(last_opaque == -1)
173                 last_opaque = pixel_opaque;
174             if(last_opaque != pixel_opaque) {
175                 const int halfwidth = dimensions.w / 2;
176                 const int halfheight = dimensions.h / 2;
177 
178                 result->kind = QuadShape;
179 
180                 next.x = dimensions.x;
181                 next.y = dimensions.y;
182                 next.w = halfwidth;
183                 next.h = halfheight;
184                 result->data.children.upleft = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
185 
186                 next.x = dimensions.x + halfwidth;
187                 next.w = dimensions.w - halfwidth;
188                 result->data.children.upright = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
189 
190                 next.x = dimensions.x;
191                 next.w = halfwidth;
192                 next.y = dimensions.y + halfheight;
193                 next.h = dimensions.h - halfheight;
194                 result->data.children.downleft = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
195 
196                 next.x = dimensions.x + halfwidth;
197                 next.w = dimensions.w - halfwidth;
198                 result->data.children.downright = (struct SDL_ShapeTree *)RecursivelyCalculateShapeTree(mode,mask,next);
199 
200                 return result;
201             }
202         }
203     }
204 
205 
206     /* If we never recursed, all the pixels in this quadrant have the same "value". */
207     result->kind = (last_opaque == SDL_TRUE ? OpaqueShape : TransparentShape);
208     result->data.shape = dimensions;
209     return result;
210 }
211 
212 SDL_ShapeTree*
SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface * shape)213 SDL_CalculateShapeTree(SDL_WindowShapeMode mode,SDL_Surface* shape)
214 {
215     SDL_Rect dimensions;
216     SDL_ShapeTree* result = NULL;
217 
218     dimensions.x = 0;
219     dimensions.y = 0;
220     dimensions.w = shape->w;
221     dimensions.h = shape->h;
222 
223     if(SDL_MUSTLOCK(shape))
224         SDL_LockSurface(shape);
225     result = RecursivelyCalculateShapeTree(mode,shape,dimensions);
226     if(SDL_MUSTLOCK(shape))
227         SDL_UnlockSurface(shape);
228     return result;
229 }
230 
231 void
SDL_TraverseShapeTree(SDL_ShapeTree * tree,SDL_TraversalFunction function,void * closure)232 SDL_TraverseShapeTree(SDL_ShapeTree *tree,SDL_TraversalFunction function,void* closure)
233 {
234     SDL_assert(tree != NULL);
235     if(tree->kind == QuadShape) {
236         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.upleft,function,closure);
237         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.upright,function,closure);
238         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.downleft,function,closure);
239         SDL_TraverseShapeTree((SDL_ShapeTree *)tree->data.children.downright,function,closure);
240     }
241     else
242         function(tree,closure);
243 }
244 
245 void
SDL_FreeShapeTree(SDL_ShapeTree ** shape_tree)246 SDL_FreeShapeTree(SDL_ShapeTree** shape_tree)
247 {
248     if((*shape_tree)->kind == QuadShape) {
249         SDL_FreeShapeTree((SDL_ShapeTree **)(char*)&(*shape_tree)->data.children.upleft);
250         SDL_FreeShapeTree((SDL_ShapeTree **)(char*)&(*shape_tree)->data.children.upright);
251         SDL_FreeShapeTree((SDL_ShapeTree **)(char*)&(*shape_tree)->data.children.downleft);
252         SDL_FreeShapeTree((SDL_ShapeTree **)(char*)&(*shape_tree)->data.children.downright);
253     }
254     SDL_free(*shape_tree);
255     *shape_tree = NULL;
256 }
257 
258 int
SDL_SetWindowShape(SDL_Window * window,SDL_Surface * shape,SDL_WindowShapeMode * shape_mode)259 SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape,SDL_WindowShapeMode *shape_mode)
260 {
261     int result;
262     if(window == NULL || !SDL_IsShapedWindow(window))
263         /* The window given was not a shapeable window. */
264         return SDL_NONSHAPEABLE_WINDOW;
265     if(shape == NULL)
266         /* Invalid shape argument. */
267         return SDL_INVALID_SHAPE_ARGUMENT;
268 
269     if(shape_mode != NULL)
270         window->shaper->mode = *shape_mode;
271     result = SDL_GetVideoDevice()->shape_driver.SetWindowShape(window->shaper,shape,shape_mode);
272     window->shaper->hasshape = SDL_TRUE;
273     if(window->shaper->userx != 0 && window->shaper->usery != 0) {
274         SDL_SetWindowPosition(window,window->shaper->userx,window->shaper->usery);
275         window->shaper->userx = 0;
276         window->shaper->usery = 0;
277     }
278     return result;
279 }
280 
281 static SDL_bool
SDL_WindowHasAShape(SDL_Window * window)282 SDL_WindowHasAShape(SDL_Window *window)
283 {
284     if (window == NULL || !SDL_IsShapedWindow(window))
285         return SDL_FALSE;
286     return window->shaper->hasshape;
287 }
288 
289 int
SDL_GetShapedWindowMode(SDL_Window * window,SDL_WindowShapeMode * shape_mode)290 SDL_GetShapedWindowMode(SDL_Window *window,SDL_WindowShapeMode *shape_mode)
291 {
292     if(window != NULL && SDL_IsShapedWindow(window)) {
293         if(shape_mode == NULL) {
294             if(SDL_WindowHasAShape(window))
295                 /* The window given has a shape. */
296                 return 0;
297             else
298                 /* The window given is shapeable but lacks a shape. */
299                 return SDL_WINDOW_LACKS_SHAPE;
300         }
301         else {
302             *shape_mode = window->shaper->mode;
303             return 0;
304         }
305     }
306     else
307         /* The window given is not a valid shapeable window. */
308         return SDL_NONSHAPEABLE_WINDOW;
309 }
310