SDL2: How to deal with transparency?

Viewed 2736

I'm experimenting SDL2 and I encounter a case I cannot manage correctly when I draw on an intermediate transparent texture.

The images I'm attempting to blend:

  1. img_w_A_channel
  2. Some solid black color with an alpha of: 255, 128, or 0
  3. window bg which is optional.

So if I represent this as layers it's as follow

img_w_A_channel    img_w_A_channel    img_w_A_channel    img_w_A_channel
                    solid_texture    half_transparent   transparent_texture
   window               window            window            window
  • On the left, I render the image directly on the window.
  • solid_texture means a black colored intermediate texture with alpha=255 where I draw the image in then I draw the texture on the window.
  • half_transparent means a black colored intermediate texture with alpha=128 where I draw the image in then I draw the texture on the window.
  • transparent_texturemeans a black colored intermediate texture with alpha=0 where I draw the image in then I draw the texture on the window.

So think about the intermediate texture as a container I want to draw in. So as the window can have a background texture like here, SDL_BLENDMODE_NONE is not an option for the intermediate texture.

And the result is:

enter image description here

As you can see the first and second image (drawn on a solid intermediate texture are identical. The third and fourth image (drawn on semi-transparent and transparent intermediate texture) are not identical.

The problem is with the image on the right. I want the result to match the image on the left. i.e. being able to draw in a transparent container and get the same result as drawing directly on the window.

My full test code is:

#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 = {20, 20, 100, 100};

#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_ARGB8888, SDL_TEXTUREACCESS_TARGET, 200, 200);
    SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
    SDL_SetRenderTarget(renderer, texture);

    // Paint it
    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, &dst );

    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 for fun (doesn't change anything on the problem) can be removed ....
    surface = IMG_Load( "./carbone.png" );
    background = SDL_CreateTextureFromSurface( renderer, surface );
    SDL_FreeSurface( surface );
    // draw background in main display
    SDL_RenderCopy( renderer, background, NULL, NULL );
    // .... until here

    surface = IMG_Load( "./image.png" );
    image = SDL_CreateTextureFromSurface( renderer, surface );
    SDL_FreeSurface( surface );
    // draw imagein main display
    SDL_RenderCopy( renderer, image, NULL, &dst );

    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 );

    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 );

    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_Delay(10000);

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

}

And to build it:

cc main.c -lSDL2 -lSDL2_image

Built with SDL 2.0.8 but it doesn't matter.

Any idea to fix this problem?

** EDIT **

So I modified my renderImageInATexture() function according to @aram's answer replacing:

SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);

by:

    SDL_BlendMode mode = SDL_ComposeCustomBlendMode(1, 1, 
                                                    SDL_BLENDOPERATION_ADD,
                                                    1, 1, 
                                                    SDL_BLENDOPERATION_ADD);
    SDL_SetTextureBlendMode(texture, mode);

I also changed my initialization directly in my previous code above to be able to save my screen as proposed by @aram. But I had to use a SDL_RENDERER_ACCELERATED to prevent a not supported operationon SDL_ComposeCustomBlendMode() call. Anyway, I get a Surface doesn't have a colorkey message. Is it a problem?

But it seems it's still that SDL_ComposeCustomBlendMode() not the solution.
First I though that background image was not important because from my tests, I was getting the same result with or without it.
Unfortunately, if @aram's answer gives me the same result as when I render my image in a solid intermediate texture: enter image description here

0 Answers
Related