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