How to correctly deal with transparency?


#1

Hello,

I’m working on a Widgets Library and I need to be able to render widgets i.e. images, to keep it simple in transparent or semi-transparent containers, these containers are then renderer in the backrgound.

So I tried to write a small example to experiment available transparency modes but unfortunately, none give me the expected result event if the best I tried is SDL_BLENDMODE_BLEND.

Here is my fully working source code:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *background = NULL;
SDL_Texture *image = NULL;
SDL_Surface* surface = NULL;

SDL_Rect dst = {0, 0, 200, 200};

#define SOLID 255
#define HALF_TRANSPARENT 128
#define TRANSPARENT 0

SDL_Texture * renderImageInATexture(SDL_Texture *image, Uint8 transparency) {

    // Create an intermediate texture
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 200, 200);
    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    SDL_SetRenderTarget(renderer, texture);

    // Paint it black with a given transparency value.
    SDL_SetRenderDrawColor(renderer, (Uint8) 0, (Uint8) 0, (Uint8) 0,(Uint8) transparency);
    SDL_RenderClear(renderer);

    // Render the image in intermediate texture
    SDL_SetRenderTarget(renderer, texture);
    SDL_RenderCopy( renderer, image, NULL, NULL );

    // The result will be rendered in the background
    SDL_SetRenderTarget(renderer, NULL);
    return texture;
}

int main(char *argc, char **argv) {

    SDL_Init( SDL_INIT_VIDEO);

     window = SDL_CreateWindow("Win", SDL_WINDOWPOS_UNDEFINED, 
                               SDL_WINDOWPOS_UNDEFINED,
                           800, 200, SDL_WINDOW_SHOWN);

    // So we can save into pngs to check the value
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    SDL_SetRenderTarget(renderer, NULL);

    // Fill the window with a texture to see if semi transparent intermediate texture is OK.
    surface = IMG_Load( "./carbone.png" );
    background = SDL_CreateTextureFromSurface( renderer, surface );
    SDL_FreeSurface( surface );
    // draw background in main display
    SDL_RenderCopy( renderer, background, NULL, NULL );

    surface = IMG_Load( "./image.png" );
    image = SDL_CreateTextureFromSurface( renderer, surface );
    SDL_FreeSurface( surface );
    // draw image in main display as reference to compare with further renderings
    SDL_RenderCopy( renderer, image, NULL, &dst);

    // Secondly, the image is rendered in a fully opaque intermediate texture.
    SDL_Texture *texture = renderImageInATexture(image, SOLID);
    // render intermediate texture in main display
    SDL_Rect dstTexture = {200, 0, 200, 200};
    SDL_RenderCopy( renderer, texture, NULL, &dstTexture );

    // Third, the image is rendered in a half transparent texture.
    SDL_Texture *texture2 = renderImageInATexture(image, HALF_TRANSPARENT);
    // render intermediate texture2 in main display
    SDL_Rect dstTexture2 = {400, 0, 200, 200};
    SDL_RenderCopy( renderer, texture2, NULL, &dstTexture2 );

    // Fourth, the image is rendered in a fully trasnparent texture, expecting the 
    // exact same result as first rendering.
    SDL_Texture *texture3 = renderImageInATexture(image, TRANSPARENT);
    // render intermediate texture3 in main display
    SDL_Rect dstTexture3 = {600, 0, 200, 200};
    SDL_RenderCopy( renderer, texture3, NULL, &dstTexture3 );

    SDL_RenderPresent(renderer);

SDL_Event e;
int wait = 1;
while (wait) {
	while (SDL_PollEvent(&e)) {
		if (e.type == SDL_QUIT) {
		wait = 0;
		}
		SDL_Delay(33);
	}
}

    SDL_DestroyTexture(image);
    SDL_DestroyTexture(bakcground); // only if you use a background
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

}

And here is the result:

As you can see the second image is similar if we don’t care about the background of course because the intermediate texture is solid.
About the next 2 ones and particularly the last one, there is a problem it’s like transparency value was decreased (255=opaque) on all the image, making it smaller because it’s fully transparent sooner than on the 1st one when we go far from the center.

And according to my tests, the more intermediate texture you add the more you lose a part of the image.

For me the white disk is on top and should be modified in any way. Its transparency is supposed to let see what’s behind, same for the intermediate texture. It’s like if the things were inverted; Maybe it is.

This problem also cause the anti aliasing to be lost and if you make this test rendering text for example, the rendering quality is very bad. (Sorry I can only put one image in my post)

I also made tests with the new SDL_ComposeCustomBlendMode() in place of SDL_BLENDMODE_BLEND
in SDL_BLENDMODE_BLEND, testing all possible combinations and nothing give me the expecting result.
It’s even quite strange because using it makes the intermediate texture fully opaque or fully transparent. Impossible to set alpha value in SDL_SetRenderDrawColor.

There is certainly a basic concept I didn’t understand so I really hope you could help me.

Here are the images used.
carbone.png https://imgur.com/a/wkQBze4
image.png https://imgur.com/JbivSIG
Al


#2

I use transparency in a game I’m working on. I just use an alpha setting for image display, which sets the amount I want. Does the job pretty fine.

I just did what the lazyfoo tutorial did for the most part.