Yamiko Hikari
Yamiko Hikari

Reputation: 127

Fully transparent window with opaque elements in SDL 2

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?

Upvotes: 6

Views: 930

Answers (1)

iloveclang
iloveclang

Reputation: 107

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.

Upvotes: 3

Related Questions