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 | |
31 | SDL_Window* |
32 | SDL_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 | |
59 | SDL_bool |
60 | SDL_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. */ |
69 | void |
70 | SDL_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 | |
125 | static SDL_ShapeTree* |
126 | RecursivelyCalculateShapeTree(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 | |
211 | SDL_ShapeTree* |
212 | SDL_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 | |
230 | void |
231 | SDL_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 | |
244 | void |
245 | SDL_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 | |
257 | int |
258 | SDL_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 | |
280 | static SDL_bool |
281 | SDL_WindowHasAShape(SDL_Window *window) |
282 | { |
283 | if (window == NULL || !SDL_IsShapedWindow(window)) |
284 | return SDL_FALSE; |
285 | return window->shaper->hasshape; |
286 | } |
287 | |
288 | int |
289 | SDL_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 | |