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