Adding multiple textures into 1

How to create a new SDL_Texture that includes other SDL_Textures, for example:

SDL_Texture* image_1 = NULL;
image_1 = IMG_LoadTexture(renderer, “image_1.png”);
SDL_Texture* image_2 = NULL;
image_2 = IMG_LoadTexture(renderer, “image_2.png”);

SDL_Texture* image_sum = NULL;
/*
From now on image_sum would be the others summ for example this way: https://prnt.sc/fhgga7
*/

(My plan is to make a card (texture of an image), and put a text (texture) on top of it. But the text message can be different in different situations, so creating a card .png is not a solution.)

1 Like

One solution is creating a blank texture (by calling SDL_CreateTexture), set the access type to SDL_TEXTUREACCESS_TARGET, to be able to use the texture as render target.
Then, in your render function, set the texture as the current render target, render your two textures (i.e, image_1 and image_2 in your example picture), reset the render target back to the default render target and then finally render the target texture that you previously rendered to.

You’ll probably create the target texture in the same size as image_1 and then you can position image_2 wherever you want on the target texture.

1 Like

This indeed sounds like a job for target textures, they’re even hardware accelerated. Beware though that you have to check with SDL_GetRendererInfo if the renderer supports them. They should be available on a wide range of system configurations. It’s mostly old Windows graphics drivers that will not offer them. There may be a few quirks depending on which backend you will use. For example, Direct3D will require you to redraw the target texture after the window size changed.

Here’s a simple example of what Naith explained.

[details=combine_with_targettexture.c]

#include "SDL.h"

static struct {
    SDL_Window * handle;
    int width, height;
    SDL_Renderer * renderer;
    SDL_Texture * texture1;
    SDL_Texture * texture2;
    SDL_Texture * texture_target;
} Window;

static void draw_to_target_texture()
{
    /* Direct the draw commands to the target texture. */
    SDL_SetRenderTarget(Window.renderer, Window.texture_target);

    /* It's always a good idea to clear the whole thing first. */
    SDL_SetRenderDrawColor(Window.renderer, 0, 0, 0, 0);
    SDL_RenderClear(Window.renderer);

    /* Let's copy the other textures onto the target texture. */
    SDL_RenderCopy(Window.renderer, Window.texture1, NULL, NULL);
    SDL_RenderCopy(Window.renderer, Window.texture2, NULL, NULL);

    /* Resetting to the default render target which is the frame buffer
       that gets displayed on screen. */
    SDL_SetRenderTarget(Window.renderer, NULL);
}

int main(int argc, char * argv[])
{
    int i;
    int done = 0;
    char title[256] = {0};
    SDL_RendererInfo info;
    SDL_Surface * tmpsurface;
    SDL_Rect src_rect, dst_rect;
    int renderer_has_target_texture_support = 0;

    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    Window.handle = SDL_CreateWindow("Loading...",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 500, SDL_WINDOW_RESIZABLE);
    Window.renderer = SDL_CreateRenderer(Window.handle, -1, 0);

    SDL_GetWindowSize(Window.handle, &Window.width, &Window.height);

    /* Checking if this renderer supports target textures */
    SDL_GetRendererInfo(Window.renderer, &info);
    renderer_has_target_texture_support = info.flags & SDL_RENDERER_TARGETTEXTURE;

    SDL_Log("Renderer %s started.", info.name);
    SDL_strlcat(title, info.name, sizeof(title));
    if (!renderer_has_target_texture_support) {
        SDL_Log(" Renderer has no target texture support!");
        return 1;
    }
    SDL_strlcat(title, ": Combine With Target Texture Example", sizeof(title));
    SDL_SetWindowTitle(Window.handle, title);

    /* Image loading part. */
    tmpsurface = SDL_LoadBMP("image_1.bmp");
    if (tmpsurface == NULL) {
        SDL_Log("Could not load image_1.bmp");
        return 1;
    } else {
        /* We'll save these values and used them for the target texture width and height */
        src_rect.w = tmpsurface->w;
        src_rect.h = tmpsurface->h;
        Window.texture1 = SDL_CreateTextureFromSurface(Window.renderer, tmpsurface);
        SDL_SetTextureBlendMode(Window.texture1, SDL_BLENDMODE_BLEND);
        SDL_FreeSurface(tmpsurface);
    }

    tmpsurface = SDL_LoadBMP("image_2.bmp");
    if (tmpsurface == NULL) {
        SDL_Log("Could not load image_2.bmp");
        return 1;
    } else {
        Window.texture2 = SDL_CreateTextureFromSurface(Window.renderer, tmpsurface);
        SDL_SetTextureBlendMode(Window.texture2, SDL_BLENDMODE_BLEND);
        SDL_FreeSurface(tmpsurface);
    }

    /* Creation of the target texture. */
    Window.texture_target = SDL_CreateTexture(Window.renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, src_rect.w, src_rect.h);
    /* Blend mode defaults to NONE, but we want it to blend it with the blue background */
    SDL_SetTextureBlendMode(Window.texture_target, SDL_BLENDMODE_BLEND);

    /* Drawing the other two textures to the target texture. */
    draw_to_target_texture();

    while (!done) {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                done = 1;
            } else if (e.type == SDL_KEYUP) {
                Uint32 sym = e.key.keysym.sym;
                if (sym == SDLK_ESCAPE) {
                    done = 1;
                } else if (sym == SDLK_f) {
                    if (SDL_GetWindowFlags(Window.handle) & SDL_WINDOW_FULLSCREEN) {
                        SDL_SetWindowFullscreen(Window.handle, SDL_FALSE);
                    } else {
                        SDL_SetWindowFullscreen(Window.handle, SDL_WINDOW_FULLSCREEN_DESKTOP);
                    }
                }
            } else if (e.type == SDL_WINDOWEVENT) {
                if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                    SDL_GetWindowSize(Window.handle, &Window.width, &Window.height);

                    /* Some backends *cough*direct3d*cough* reset when the window size changes.
                       Target textures are not saved and have to be redrawn. */
                    draw_to_target_texture();
                }
            }
        }

        SDL_SetRenderDrawColor(Window.renderer, 0, 0, 200, 0);
        SDL_RenderClear(Window.renderer);

        SDL_RenderCopy(Window.renderer, Window.texture_target, NULL, NULL);

        SDL_RenderPresent(Window.renderer);

        SDL_Delay(3);
    }

    SDL_DestroyTexture(Window.texture1);
    SDL_DestroyTexture(Window.texture2);
    SDL_DestroyTexture(Window.texture_target);
    SDL_DestroyRenderer(Window.renderer);
    SDL_DestroyWindow(Window.handle);
    SDL_Quit();

    return 0;
}
```[/details]

There's also the option to do the combining with `SDL_Surface`s. This doesn't offer GPU acceleration or the other renderer options, but it will be supported on all systems. If you just need to prepare something at the start of your program and never change it, this may be a practical solution.

[details=combine_with_surface.c]
```C
#include "SDL.h"

static struct {
    SDL_Window * handle;
    int width, height;
    SDL_Renderer * renderer;
    SDL_Texture * texture1;
} Window;


int main(int argc, char * argv[])
{
    int i;
    int done = 0;
    char title[256] = {0};
    SDL_RendererInfo info;
    SDL_Surface * surface1;
    SDL_Surface * surface2;
    SDL_Rect src_rect, dst_rect;

    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
    Window.handle = SDL_CreateWindow("Loading...",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 500, SDL_WINDOW_RESIZABLE);
    Window.renderer = SDL_CreateRenderer(Window.handle, -1, 0);

    SDL_GetWindowSize(Window.handle, &Window.width, &Window.height);

    SDL_GetRendererInfo(Window.renderer, &info);

    SDL_Log("Renderer %s started.", info.name);
    SDL_strlcat(title, info.name, sizeof(title));
    SDL_strlcat(title, ": Combine With Surface Example", sizeof(title));
    SDL_SetWindowTitle(Window.handle, title);

    /* Image loading part. */
    surface1 = SDL_LoadBMP("image_1.bmp");
    if (surface1 == NULL) {
        SDL_Log("Could not load image_1.bmp");
        return 1;
    }

    surface2 = SDL_LoadBMP("image_2.bmp");
    if (surface2 == NULL) {
        SDL_Log("Could not load image_2.bmp");
        return 1;
    }

    /* Drawing the second surface to the first one. */
    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.w = surface1->w;
    dst_rect.h = surface1->h;
    SDL_BlitScaled(surface2, NULL, surface1, &dst_rect);

    /* Creation of the texture from the combined surface. */
    Window.texture1 = SDL_CreateTextureFromSurface(Window.renderer, surface1);
    /* The blend mode gets copied from the surface, but let's just force it here. */
    SDL_SetTextureBlendMode(Window.texture1, SDL_BLENDMODE_BLEND);

    SDL_FreeSurface(surface1);
    SDL_FreeSurface(surface2);


    while (!done) {
        SDL_Event e;
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                done = 1;
            } else if (e.type == SDL_KEYUP) {
                Uint32 sym = e.key.keysym.sym;
                if (sym == SDLK_ESCAPE) {
                    done = 1;
                } else if (sym == SDLK_f) {
                    if (SDL_GetWindowFlags(Window.handle) & SDL_WINDOW_FULLSCREEN) {
                        SDL_SetWindowFullscreen(Window.handle, SDL_FALSE);
                    } else {
                        SDL_SetWindowFullscreen(Window.handle, SDL_WINDOW_FULLSCREEN_DESKTOP);
                    }
                }
            } else if (e.type == SDL_WINDOWEVENT) {
                if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
                    SDL_GetWindowSize(Window.handle, &Window.width, &Window.height);
                }
            }
        }

        SDL_SetRenderDrawColor(Window.renderer, 0, 0, 200, 0);
        SDL_RenderClear(Window.renderer);

        SDL_RenderCopy(Window.renderer, Window.texture1, NULL, NULL);

        SDL_RenderPresent(Window.renderer);

        SDL_Delay(3);
    }

    SDL_DestroyTexture(Window.texture1);
    SDL_DestroyRenderer(Window.renderer);
    SDL_DestroyWindow(Window.handle);
    SDL_Quit();

    return 0;
}
```[/details]

Since you specifically said "sum" you may also want to check out the `SDL_BLENDMODE_ADD` blend mode. This does adding instead of alpha blending.
1 Like

Thanks for the answer, the problem is solved, here is my solution, by your guidance:

SDL_Texture* image_sum = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 60, 60);
SDL_SetRenderTarget(renderer, image_sum);

SDL_Texture *image_1;
SDL_Texture *image_2;
image_1 = IMG_LoadTexture(renderer, "image_1.png");
image_2 = IMG_LoadTexture(renderer, "image_2.png");

SDL_RenderClear(renderer);

SDL_Rect image_1_rect;
image_1_rect.x=0;
image_1_rect.y=0;
image_1_rect.w=60;
image_1_rect.h=60;

SDL_Rect image_2_rect;
image_2_rect.x=20;
image_2_rect.y=20;
image_2_rect.w=20;
image_2_rect.h=20;

SDL_RenderCopy(renderer, image_1, NULL, &image_1_rect);
SDL_RenderCopy(renderer, image_2, NULL, &image_2_rect);
SDL_SetRenderTarget(renderer, NULL);

SDL_Rect image_sum_rect;
image_sum_rect.x=0;
image_sum_rect.y=0;
image_sum_rect.w=60;
image_sum_rect.h=60;

SDL_RenderCopy(renderer, image_sum, NULL, &image_sum_rect);
SDL_RenderPresent(renderer);