Fully transparent window with opaque elements in SDL 2
Asked Answered
C

1

6

I am trying to create an SDL window which is by itself fully transparent, but when an image is rendered onto it, the image is fully opaque and the alpha channel of the image is conserved (so the transparent pixels of the image remain transparent). So the result would be just an image on the screen, with no container around it (Imagine sticking a real-life sticker on your screen)

So here is the simplified code for that:

#include <stdio.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_syswm.h>
#include <iostream>
#include <Windows.h>
#include <string>

SDL_Window* window;
SDL_Renderer* renderer;

SDL_Texture* image;
int imageWidth = 0;
int imageHeight = 0;

bool init()
{
    //Initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO) != 0)
    {
        printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
        return false;
    }

    //Set texture filtering to linear
    if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
    {
        printf("Warning: Linear texture filtering not enabled!");
    }

    //Create window
    window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 500, 300, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);
    if (window == NULL)
    {
        printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
        return false;
    }

    //Create renderer for window
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (renderer == NULL)
    {
        printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
        return false;
    }

    //Initialize renderer color
    SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);

    //Initialize PNG loading
    int imgFlags = IMG_INIT_PNG;
    if (!(IMG_Init(imgFlags) & imgFlags))
    {
        printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
        return false;
    }
    return true;
}

bool loadImage(const std::string& path)
{
    //The final texture
    SDL_Texture* newImage = NULL;

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load(path.c_str());
    if (!loadedSurface)
    {
        printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
        return false;
    }

    //Create texture from surface pixels
    newImage = SDL_CreateTextureFromSurface(renderer, loadedSurface);
    if (!newImage)
    {
        printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
        return false;
    }

    imageWidth = loadedSurface->w;
    imageHeight = loadedSurface->h;

    //Get rid of old loaded surface
    SDL_FreeSurface(loadedSurface);

    image = newImage;
    return true;
}


void displayImage()
{
    SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
    SDL_RenderClear(renderer);

    SDL_SetWindowSize(window, imageWidth, imageHeight);
    SDL_Rect renderQuad = { 0, 0, imageWidth, imageHeight };

    SDL_RenderCopy(renderer, image, NULL, &renderQuad);
    SDL_RenderPresent(renderer);
}


// Makes a window transparent by setting a transparency color.
bool windowColorKey(SDL_Window* window, COLORREF colorKey) {

    SDL_SysWMinfo wmInfo;
    SDL_VERSION(&wmInfo.version);  // Initialize wmInfo
    SDL_GetWindowWMInfo(window, &wmInfo);
    HWND hWnd = wmInfo.info.win.window;

    // Change window type to layered
    SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);

    // Set transparency color
    return SetLayeredWindowAttributes(hWnd, colorKey, 0, LWA_COLORKEY);
}


int main(int argc, char* args[]) {

    // Initialize SDL: Create a window and renderer
    init();

    // Load the image 
    loadImage("example.png");

    // Color key the background of the window (magenta)
    windowColorKey(window, RGB(255, 0, 255));

    // Render the image on the widnow with magenta for backround color
    displayImage();

    // Check for events
    bool windowActive = true;
    SDL_Event event;
    while (windowActive)
    {
        while (SDL_PollEvent(&event) != 0)
        {
            if (event.type == SDL_QUIT || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { windowActive = false; }
        }
    }

    return 0;
}

Using SDL_SetWindowOpacity() won't work because all the elements of the window transparent too.

For now the only way I know to achieve this goal is using color keying, but unfortunately it doesn't work that well, since some of the images will contain the color you use for color keying, and additionally the images will have a small border of the color you used for color keying all around the image. It's thin but annoying.

Color keying

I am thinking of using the features of a layered window in some other way but cannot find or understand how to.

So, the question is how to make the window itself fully transparent, while having full control over the transparency of the elements of the window?

Curson answered 19/5, 2021 at 19:46 Comment(1)
It looks like you're using Windows. Maybe you can get a handle to the native window as described here: #24118483 and do this in a platform specific way. As @iloveclang says in their answer, the SDL maintainers aren't going to implement a platform agnostic way to achieve what you're looking forRubalcava
O
3

It appears SDL guys are aware of this popular request and the issue:

https://github.com/libsdl-org/SDL/issues/1872

and don't intend to do anything about it outside of SDL_SetWindowOpacity(), which is nowhere near as useful or cool as a transparrent background would be.

Obsequies answered 13/1, 2024 at 17:25 Comment(1)
It sounds like their rationale is justified. SDL aims to be compatible with many platforms and if this can't be achieved on most of them they wouldn't implement it on the few that doRubalcava

© 2022 - 2025 — McMap. All rights reserved.