SDL_shape.c (11309B)
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_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 }