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:
- img_w_A_channel
- Some solid black color with an alpha of:
255,128, or0 - 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_texturemeans a black colored intermediate texture withalpha=255where I draw the image in then I draw the texture on the window.half_transparentmeans a black colored intermediate texture withalpha=128where I draw the image in then I draw the texture on the window.transparent_texturemeans a black colored intermediate texture withalpha=0where 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:
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:

