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