Skip to content

Commit

Permalink
Add shaped window based on SDL_WINDOW_TRANSPARENT
Browse files Browse the repository at this point in the history
  • Loading branch information
1bsyl authored and slouken committed Nov 23, 2023
1 parent 135eeac commit 03631a8
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 25 deletions.
231 changes: 230 additions & 1 deletion src/sdl2_compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -5568,16 +5568,116 @@ SDL_CreateWindowFrom(const void *data)
return window;
}

static SDL_Window *g_shaped_window = NULL;
static SDL_WindowShapeMode g_shape_mode;
static Uint8 *g_bitmap = NULL;
static int g_bitmap_w = 0, g_bitmap_h = 0;
static SDL_Surface *g_shape_surface = NULL;
static SDL_Texture *g_shape_texture = NULL;

static void shaped_window_cleanup(void)
{
g_shaped_window = NULL;
SDL3_zero(g_shape_mode);
if (g_bitmap) {
SDL3_free(g_bitmap);
g_bitmap = NULL;
}
g_bitmap_w = 0;
g_bitmap_h = 0;

if (g_shape_surface) {
SDL3_DestroySurface(g_shape_surface);
g_shape_surface = NULL;
}

if (g_shape_texture) {
SDL3_DestroyTexture(g_shape_texture);
g_shape_texture = NULL;
}
}

/* REQUIRES that bitmap point to a w-by-h bitmap with ppb pixels-per-byte. */
static void SDL_CalculateShapeBitmap(SDL_WindowShapeMode mode, SDL_Surface *shape, Uint8 *bitmap, Uint8 ppb)
{
int x = 0;
int y = 0;
Uint8 r = 0, g = 0, b = 0, alpha = 0;
Uint8 *pixel = NULL;
Uint32 pixel_value = 0, mask_value = 0;
size_t bytes_per_scanline = (size_t)(shape->w + (ppb - 1)) / ppb;
Uint8 *bitmap_scanline;
SDL_Color key;

if (SDL_MUSTLOCK(shape)) {
SDL_LockSurface(shape);
}

SDL_memset(bitmap, 0, shape->h * bytes_per_scanline);

for (y = 0; y < shape->h; y++) {
bitmap_scanline = bitmap + y * bytes_per_scanline;
for (x = 0; x < shape->w; x++) {
alpha = 0;
pixel_value = 0;
pixel = (Uint8 *)(shape->pixels) + (y * shape->pitch) + (x * shape->format->BytesPerPixel);
switch (shape->format->BytesPerPixel) {
case (1):
pixel_value = *pixel;
break;
case (2):
pixel_value = *(Uint16 *)pixel;
break;
case (3):
pixel_value = *(Uint32 *)pixel & (~shape->format->Amask);
break;
case (4):
pixel_value = *(Uint32 *)pixel;
break;
}
SDL_GetRGBA(pixel_value, shape->format, &r, &g, &b, &alpha);
switch (mode.mode) {
case (ShapeModeDefault):
mask_value = (alpha >= 1 ? 1 : 0);
break;
case (ShapeModeBinarizeAlpha):
mask_value = (alpha >= mode.parameters.binarizationCutoff ? 1 : 0);
break;
case (ShapeModeReverseBinarizeAlpha):
mask_value = (alpha <= mode.parameters.binarizationCutoff ? 1 : 0);
break;
case (ShapeModeColorKey):
key = mode.parameters.colorKey;
mask_value = ((key.r != r || key.g != g || key.b != b) ? 1 : 0);
break;
}
bitmap_scanline[x / ppb] |= mask_value << (x % ppb);
}
}

if (SDL_MUSTLOCK(shape)) {
SDL_UnlockSurface(shape);
}
}



DECLSPEC SDL_Window * SDLCALL
SDL_CreateShapedWindow(const char *title, unsigned int x, unsigned int y, unsigned int w, unsigned int h, Uint32 flags)
{
SDL_Window *window;
int hidden = flags & SDL_WINDOW_HIDDEN;

if (g_shaped_window != NULL) {
SDL3_SetError("only 1 shaped window");
return NULL;
}

flags &= ~SDL2_WINDOW_SHOWN;
flags |= SDL_WINDOW_HIDDEN;
flags |= SDL_WINDOW_TRANSPARENT;

window = SDL3_CreateShapedWindow(title, (int)w, (int)h, flags);
window = SDL3_CreateWindow(title, (int)w, (int)h, flags);
if (window) {
if (!SDL_WINDOWPOS_ISUNDEFINED(x) || !SDL_WINDOWPOS_ISUNDEFINED(y)) {
SDL3_SetWindowPosition(window, (int)x, (int)y);
Expand All @@ -5586,9 +5686,95 @@ SDL_CreateShapedWindow(const char *title, unsigned int x, unsigned int y, unsign
SDL3_ShowWindow(window);
}
}

shaped_window_cleanup();
g_shaped_window = window;

return window;
}

DECLSPEC SDL_bool SDLCALL
SDL_IsShapedWindow(const SDL_Window *window)
{
if (window == NULL) {
return SDL_FALSE;
}
if (window == g_shaped_window) {
return SDL_TRUE;
}
return SDL_FALSE;
}

DECLSPEC int SDLCALL
SDL_SetWindowShape(SDL_Window *window,SDL_Surface *shape, SDL_WindowShapeMode *shape_mode)
{
if (window == NULL) {
return SDL_NONSHAPEABLE_WINDOW;
}

if (window != g_shaped_window) {
return SDL_NONSHAPEABLE_WINDOW;
}

if (shape == NULL) {
return SDL_INVALID_SHAPE_ARGUMENT;
}

if (shape_mode == NULL) {
return SDL_INVALID_SHAPE_ARGUMENT;
}

shaped_window_cleanup();
g_shaped_window = window;
g_shape_mode = *shape_mode;

g_bitmap_w = shape->w;
g_bitmap_h = shape->h;
g_bitmap = (Uint8*) SDL_malloc(shape->w * shape->h);
if (g_bitmap == NULL) {
shaped_window_cleanup();
g_shaped_window = window;
return SDL3_OutOfMemory();
}

SDL_CalculateShapeBitmap(*shape_mode, shape, g_bitmap, 1);

g_shape_surface = SDL3_CreateSurface(g_bitmap_w, g_bitmap_h, SDL_PIXELFORMAT_ABGR8888);
if (g_shape_surface) {
int x, y, i = 0;
Uint32 *ptr = (Uint32 *)g_shape_surface->pixels;
for (y = 0; y < g_bitmap_h; y++) {
for (x = 0; x < g_bitmap_w; x++) {
Uint8 val = g_bitmap[i++];
if (val == 0) {
ptr[x] = 0;
} else {
ptr[x] = 0xffffffff;
}
}
ptr = (Uint32 *)((Uint8 *)ptr + g_shape_surface->pitch);
}
}

return 0;
}

DECLSPEC int SDLCALL
SDL_GetShapedWindowMode(SDL_Window *window, SDL_WindowShapeMode *shape_mode)
{
if (window == NULL) {
return SDL_NONSHAPEABLE_WINDOW;
}
if (window != g_shaped_window) {
return SDL_NONSHAPEABLE_WINDOW;
}

if (shape_mode) {
*shape_mode = g_shape_mode;
}
return 0;
}

DECLSPEC int SDLCALL
SDL_SetWindowFullscreen(SDL_Window *window, Uint32 flags)
{
Expand Down Expand Up @@ -5707,6 +5893,44 @@ SDL_UnionFRect(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result)
DECLSPEC void SDLCALL
SDL_RenderPresent(SDL_Renderer *renderer)
{
/* Apply the shape */
if (g_shape_surface && g_shaped_window == SDL3_GetRenderWindow(renderer)) {
SDL_RendererInfo info;
SDL3_GetRendererInfo(renderer, &info);

if (info.flags & SDL_RENDERER_SOFTWARE) {
if (g_bitmap) {
int x, y, i = 0;
Uint8 r, g, b, a;
SDL3_GetRenderDrawColor(renderer, &r, &g, &b, &a);
SDL3_SetRenderDrawColor(renderer, 0, 0, 0, 0);
for (y = 0; y < g_bitmap_h; y++) {
for (x = 0; x < g_bitmap_w; x++) {
Uint8 val = g_bitmap[i++];
if (val == 0) {
SDL3_RenderPoint(renderer, (float)x, (float)y);
}
}
}
SDL3_SetRenderDrawColor(renderer, r, g, b, a);
}
} else {
if (g_shape_texture == NULL) {
SDL_BlendMode bm;

g_shape_texture = SDL3_CreateTextureFromSurface(renderer, g_shape_surface);

/* if Alpha is 0, set all to 0, else leave unchanged. */
bm = SDL3_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDOPERATION_ADD);

SDL3_SetTextureBlendMode(g_shape_texture, bm);
}
SDL3_RenderTexture(renderer, g_shape_texture, NULL, NULL);
}
}

SDL3_RenderPresent(renderer);
}

Expand Down Expand Up @@ -5876,6 +6100,11 @@ SDL_SetWindowMouseGrab(SDL_Window *window, SDL_bool grabbed)
DECLSPEC void SDLCALL
SDL_DestroyWindow(SDL_Window *window)
{
if (window == g_shaped_window) {
shaped_window_cleanup();
g_shaped_window = NULL;
}

SDL3_DestroyWindow(window);
}

Expand Down
23 changes: 23 additions & 0 deletions src/sdl2_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,27 @@ struct SDL_SysWMinfo

typedef struct SDL_SysWMinfo SDL_SysWMinfo;


#define SDL_NONSHAPEABLE_WINDOW -1
#define SDL_INVALID_SHAPE_ARGUMENT -2
#define SDL_WINDOW_LACKS_SHAPE -3

typedef enum {
ShapeModeDefault,
ShapeModeBinarizeAlpha,
ShapeModeReverseBinarizeAlpha,
ShapeModeColorKey
} WindowShapeMode;

#define SDL_SHAPEMODEALPHA(mode) (mode == ShapeModeDefault || mode == ShapeModeBinarizeAlpha || mode == ShapeModeReverseBinarizeAlpha)
typedef union {
Uint8 binarizationCutoff;
SDL_Color colorKey;
} SDL_WindowShapeParams;

typedef struct SDL_WindowShapeMode {
WindowShapeMode mode;
SDL_WindowShapeParams parameters;
} SDL_WindowShapeMode;

#endif /* sdl2_compat_h */
20 changes: 0 additions & 20 deletions src/sdl3_include_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
#define SDL_CreateRWLock IGNORE_THIS_VERSION_OF_SDL_CreateRWLock
#define SDL_CreateRenderer IGNORE_THIS_VERSION_OF_SDL_CreateRenderer
#define SDL_CreateSemaphore IGNORE_THIS_VERSION_OF_SDL_CreateSemaphore
#define SDL_CreateShapedWindow IGNORE_THIS_VERSION_OF_SDL_CreateShapedWindow
#define SDL_CreateSoftwareRenderer IGNORE_THIS_VERSION_OF_SDL_CreateSoftwareRenderer
#define SDL_CreateSurface IGNORE_THIS_VERSION_OF_SDL_CreateSurface
#define SDL_CreateSurfaceFrom IGNORE_THIS_VERSION_OF_SDL_CreateSurfaceFrom
Expand Down Expand Up @@ -340,7 +339,6 @@
#define SDL_GetSensorNonPortableType IGNORE_THIS_VERSION_OF_SDL_GetSensorNonPortableType
#define SDL_GetSensorType IGNORE_THIS_VERSION_OF_SDL_GetSensorType
#define SDL_GetSensors IGNORE_THIS_VERSION_OF_SDL_GetSensors
#define SDL_GetShapedWindowMode IGNORE_THIS_VERSION_OF_SDL_GetShapedWindowMode
#define SDL_GetSurfaceAlphaMod IGNORE_THIS_VERSION_OF_SDL_GetSurfaceAlphaMod
#define SDL_GetSurfaceBlendMode IGNORE_THIS_VERSION_OF_SDL_GetSurfaceBlendMode
#define SDL_GetSurfaceClipRect IGNORE_THIS_VERSION_OF_SDL_GetSurfaceClipRect
Expand Down Expand Up @@ -446,7 +444,6 @@
#define SDL_IsDeXMode IGNORE_THIS_VERSION_OF_SDL_IsDeXMode
#define SDL_IsGamepad IGNORE_THIS_VERSION_OF_SDL_IsGamepad
#define SDL_IsJoystickVirtual IGNORE_THIS_VERSION_OF_SDL_IsJoystickVirtual
#define SDL_IsShapedWindow IGNORE_THIS_VERSION_OF_SDL_IsShapedWindow
#define SDL_IsTablet IGNORE_THIS_VERSION_OF_SDL_IsTablet
#define SDL_JoystickConnected IGNORE_THIS_VERSION_OF_SDL_JoystickConnected
#define SDL_JoystickEventsEnabled IGNORE_THIS_VERSION_OF_SDL_JoystickEventsEnabled
Expand Down Expand Up @@ -637,7 +634,6 @@
#define SDL_SetWindowOpacity IGNORE_THIS_VERSION_OF_SDL_SetWindowOpacity
#define SDL_SetWindowPosition IGNORE_THIS_VERSION_OF_SDL_SetWindowPosition
#define SDL_SetWindowResizable IGNORE_THIS_VERSION_OF_SDL_SetWindowResizable
#define SDL_SetWindowShape IGNORE_THIS_VERSION_OF_SDL_SetWindowShape
#define SDL_SetWindowSize IGNORE_THIS_VERSION_OF_SDL_SetWindowSize
#define SDL_SetWindowTitle IGNORE_THIS_VERSION_OF_SDL_SetWindowTitle
#define SDL_SetWindowsMessageHook IGNORE_THIS_VERSION_OF_SDL_SetWindowsMessageHook
Expand Down Expand Up @@ -1240,10 +1236,6 @@
#undef SDL_CreateSemaphore
#endif

#ifdef SDL_CreateShapedWindow
#undef SDL_CreateShapedWindow
#endif

#ifdef SDL_CreateSoftwareRenderer
#undef SDL_CreateSoftwareRenderer
#endif
Expand Down Expand Up @@ -2268,10 +2260,6 @@
#undef SDL_GetSensors
#endif

#ifdef SDL_GetShapedWindowMode
#undef SDL_GetShapedWindowMode
#endif

#ifdef SDL_GetSurfaceAlphaMod
#undef SDL_GetSurfaceAlphaMod
#endif
Expand Down Expand Up @@ -2692,10 +2680,6 @@
#undef SDL_IsJoystickVirtual
#endif

#ifdef SDL_IsShapedWindow
#undef SDL_IsShapedWindow
#endif

#ifdef SDL_IsTablet
#undef SDL_IsTablet
#endif
Expand Down Expand Up @@ -3456,10 +3440,6 @@
#undef SDL_SetWindowResizable
#endif

#ifdef SDL_SetWindowShape
#undef SDL_SetWindowShape
#endif

#ifdef SDL_SetWindowSize
#undef SDL_SetWindowSize
#endif
Expand Down
4 changes: 0 additions & 4 deletions src/sdl3_syms.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,6 @@ SDL3_SYM(SDL_RWops*,RWFromMem,(void *a, size_t b),(a,b),return)
SDL3_SYM(SDL_RWops*,RWFromConstMem,(const void *a, size_t b),(a,b),return)
SDL3_SYM(SDL_RWops*,CreateRW,(void),(),return)
SDL3_SYM(void,DestroyRW,(SDL_RWops *a),(a),)
SDL3_SYM(SDL_Window*,CreateShapedWindow,(const char *a, int b, int c, Uint32 d),(a,b,c,d),return)
SDL3_SYM_PASSTHROUGH(SDL_bool,IsShapedWindow,(const SDL_Window *a),(a),return)
SDL3_SYM_PASSTHROUGH(int,SetWindowShape,(SDL_Window *a, SDL_Surface *b, SDL_WindowShapeMode *c),(a,b,c),return)
SDL3_SYM_PASSTHROUGH(int,GetShapedWindowMode,(SDL_Window *a, SDL_WindowShapeMode *b),(a,b),return)
SDL3_SYM_PASSTHROUGH(void*,malloc,(size_t a),(a),return)
SDL3_SYM_PASSTHROUGH(void*,calloc,(size_t a, size_t b),(a,b),return)
SDL3_SYM_PASSTHROUGH(void*,realloc,(void *a, size_t b),(a,b),return)
Expand Down

0 comments on commit 03631a8

Please sign in to comment.