This is probably rather simple problem, but after an hour of searching and trying I still didn't manage to solve it.
I have two png files. One is a background image and second is foreground. The foreground has an alpha channel. I want to display foreground on top of background.
I'm loading foreground using:
SDL_Surface *clip = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0xff);
SDL_Rect rect = { x, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
SDL_BlitSurface(map, &rect, clip, NULL);
*block = SDL_CreateTextureFromSurface(gRenderer, clip);
Where map is some SDL_Surface.
I'm loadin backgroun using:
SDL_Surface* loadedSurface = IMG_Load(path);
//Create texture from surface pixels
SDL_Texture* newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
SDL_FreeSurface(loadedSurface);
Then I trying to connect them:
SDL_RenderCopy(gRenderer, background, NULL, &cur);
SDL_RenderCopy(gRenderer, map, NULL, &cur);
But it results in foreground image with black background. What am i doing wrong?
You should add these 2 lines,
Uint32 colorkey = SDL_MapRGB(loadedSurface->format, 0, 0, 0);
SDL_SetColorKey(loadedSurface, SDL_TRUE, colorkey);
before this line in your code
SDL_Texture* newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
Related
I want to copy multiple surfaces (created with TTF_*) to a single texture, and I can't seem to get that resulting texture to render onto the window with transparency handled correctly.
static void example(void) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
TTF_Init();
SDL_Window* w = SDL_CreateWindow("", 0, 0, 200, 200, 0);
SDL_Renderer* r = SDL_CreateRenderer(w, -1, 0);
TTF_Font* f = TTF_OpenFont(MY_FONT, 100);
SDL_Color c = {.r = 0, .g = 255, .b = 0, .a = 255};
SDL_Surface* s = TTF_RenderGlyph32_Blended(f, 'A', c);
SDL_Texture* t = SDL_CreateTextureFromSurface(r, s);
#ifdef RENDER_COPY
SDL_Texture* t2 = SDL_CreateTexture(
r,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET,
s->w,
s->h);
SDL_SetRenderTarget(r, t2);
SDL_RenderCopy(r, t, NULL, NULL);
SDL_SetRenderTarget(r, NULL);
t = t2;
#endif
#ifdef RENDER_MEMCPY
SDL_Texture* t2 = SDL_CreateTexture(
r,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
s->w,
s->h);
SDL_Surface* s2;
sdl_try(SDL_LockTextureToSurface(t2, NULL, &s2));
memcpy(s2->pixels, s->pixels, s->w * s->h * sizeof(SDL_Color));
SDL_UnlockTexture(t2);
t = t2;
#endif
#ifdef RENDER_BLEND
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND);
#endif
SDL_SetRenderDrawColor(r, 255, 255, 255, 255);
SDL_RenderClear(r);
SDL_Rect rect = {.x = 0, .y = 0};
SDL_QueryTexture(t, NULL, NULL, &rect.w, &rect.h);
SDL_RenderCopy(r, t, &rect, &rect);
SDL_RenderPresent(r);
SDL_Event event;
do { SDL_WaitEvent(&event); } while (event.type != SDL_KEYDOWN);
}
Without RENDER_COPY, I get a texture (created via SDL_CreateTextureFromSurface) that blends correctly onto a render target (this is what I want, but with multiple surfaces combined into one texture.)
With RENDER_COPY (i.e. a second texture is created and then copied onto) the background of the texture is black. This is a contrived example since there is only one surface being copied, but I want to copy multiple surfaces to t2.)
With RENDER_BLEND, the black is mostly gone but it's as if the texture was blended onto a black background.
Is there a way to create a texture that can be set completely transparent instead of a solid color? I've also tried to set the pixels directly (RENDER_MEMCPY) but that just ends up being a solid color as it appears the alpha in each pixel is ignored:
SDL version is 2.0.20.
Figured it out. When doing SDL_RenderCopy from the first texture to the second, the blend mode on the first texture should be set to none:
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_NONE);
Now when the second texture is copied (with SDL_BLENDMODE_BLEND) the edges don't have the black artifacts.
I would like help with building a layered rendering system in SDL2.
I have a first layer containing a map of Paris with its roads.
I need to draw a line between two points on this map - the problem occurs when the before state of this line does not disappear.
I need to draw this line over the map and keep this.
How do I make s a system to save the map state whithout any overlaid lines drawn, and such that frame by frame I can show the map with the new state of the line overlaid on top of this?
Solved, below an example !! Thank you all
SDL_Window *window;
SDL_Renderer *render;
SDL_Texture *map; //map texture (my layer)
window = SDL_CreateWindow("Test window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
map = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480); //Creating a texture
/*Map is a red background stored in map texture*/
SDL_SetRenderDrawColor(render, 255, 0, 0, 255);
SDL_SetRenderTarget(render, map);
SDL_RenderClear(render);
SDL_SetRenderTarget(render, NULL);
/*Seting the line color*/
SDL_SetRenderDrawColor(render, 0, 255, 0, 255);
/*Coping the map texture to the render and drawing a green line on top of this*/
SDL_RenderCopy(render, map, NULL, NULL);
SDL_RenderDrawLine(render, 0, 0, 640, 480);
SDL_RenderPresent(render);
SDL_Delay(2000);
/*Another line*/
SDL_RenderCopy(render, map, NULL, NULL);
SDL_RenderDrawLine(render, 0, 480, 640, 0);
SDL_RenderPresent(render);
SDL_Delay(2000);
SDL_DestroyWindow(window);
SDL_DestroyRenderer(render);
SDL_Quit();
I try to do it by using SDL_RenderCopy()
But I just get a black box.
This is my code.
static SDL_Texture* GetAreaTextrue(SDL_Rect rect, SDL_Renderer* renderer, SDL_Texture* source)
{
SDL_Texture* result = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect.w, rect.h);
SDL_SetRenderTarget(renderer, result);
SDL_RenderCopy(renderer, source, &rect, NULL);
SDL_RenderPresent(renderer);
return result;
}
What is the proper operation?
EDIT:
What you want to do here is render a part of the texture to the screen.
There is a way to do this by using SDL_RenderCopy, but by doing this you just "take" the part you want from your texture and "slap" it onto your screen.
What you want (by my understanding) is to take a part of a texture and save it into another texture variable that afterwards can be rendered to the screen.
The first solution to the problem goes like this:
// load your image in a SDL_Texture variable (myTexture for example)
// if the image is (256,128) and you want to render only the first half
// you need a rectangle of the same size as that part of the image
SDL_Rect imgPartRect;
imgPartRect.x = 0;
imgPartRect.y = 0;
imgPartRect.w = 32;
imgPartRect.h = 32;
// and the program loop should have a draw block looking like this:
SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderClear( renderer );
SDL_RenderCopy( renderer, myTexture, &imgPartRect, NULL );
SDL_RenderPresent( renderer );
The approach you are trying to use has an intermediate texture that you render on and afterwards you render that texture to the screen. The problem here is that you set the renderer to draw on the texture you just created but you never reset your renderer to use the default target (the screen).
As you can see in the SDL documentation here the second parameter receives the texture that you want your renerer to draw on, or NULL if you want to reset it to the default target (the screen).
int SDL_SetRenderTarget(SDL_Renderer* renderer, SDL_Texture* texture)
Where:
renderer
the rendering context
texture
the targeted texture, which must be created with the SDL_TEXTUREACCESS_TARGET flag, or NULL for the default render target
Lets use the same example as before:
// first off let's build the function that you speak of
SDL_Texture* GetAreaTextrue(SDL_Rect rect, SDL_Renderer* renderer, SDL_Texture* source)
{
SDL_Texture* result = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect.w, rect.h);
SDL_SetRenderTarget(renderer, result);
SDL_RenderCopy(renderer, source, &rect, NULL);
// the folowing line should reset the target to default(the screen)
SDL_SetRenderTarget(renderer, NULL);
// I also removed the RenderPresent funcion as it is not needed here
return result;
}
// load your image in a SDL_Texture variable (myTexture for example)
// if the image is (256,128) and you want to render only the first half
// you need a rectangle of the same size as that part of the image
SDL_Rect imgPartRect;
imgPartRect.x = 0;
imgPartRect.y = 0;
imgPartRect.w = 32;
imgPartRect.h = 32;
// now we use the function from above to build another texture
SDL_Texture* myTexturePart = GetAreaTextrue( imgPartRect, renderer, myTexture );
// and the program loop should have a draw block looking like this:
SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderClear( renderer );
// here we render the whole newly created texture as it contains only a part of myTexture
SDL_RenderCopy( renderer, myTexturePart, NULL, NULL );
SDL_RenderPresent( renderer );
I don't know what you want to do, but I highly recommend the first way though.
I'm trying to make a very little and simple snippet with SDL. This one works like a charm :
SDL_Window * window = SDL_CreateWindow("SDLTest", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_SWSURFACE);
screen = SDL_GetWindowSurface(window);
SDL_Color color={0,0,0};
TTF_GlyphMetrics(font, ch, &minx, &maxx, &miny, &maxy, NULL);
SDL_Surface * car =TTF_RenderGlyph_Blended(font,ch,color);
SDL_Rect textRect = {offsetX, offsetY, 0, 0};
if(SDL_BlitSurface( car, NULL, glyph, &screen ))
qDebug() << SDL_GetError();
and this one doesn't work at all :
SDL_Surface * glyph = NULL;
SDL_Surface * car = TTF_RenderGlyph_Blended(font,ch,color);
qDebug() << TTF_GetError();
SDL_Rect textRect = {0, 0, car->w, car->h};
if(SDL_BlitSurface( car, NULL, glyph, &textRect ))
qDebug() << SDL_GetError();
TTF_GetError() return nothing so I assume TTF_RenderGlyph_Blended works well and SDL_GetError() send me this :
SDL_UpperBlit: passed a NULL surface
::::::::::::::::: EDIT ::::::::::::::::::
Ok, I've fix the NULL problem, but the blit is not good yet:
ch = 66;
SDL_Surface * glyph = TTF_RenderUTF8_Blended(font, "Z", color);
SDL_UnlockSurface(glyph);
SDL_Surface * car = TTF_RenderGlyph_Blended(font,ch,color);
SDL_Rect textRect = {0, 0, car->w, car->h};
qDebug() << SDL_BlitSurface(car, NULL, glyph, &textRect);
qDebug() << SDL_BlitSurface(glyph, NULL, screen, &textRect);
Should display B but go Z instead...
SDL_BlitSurface requires source surface (your car variable) and destination surface (your glyph variable). Your first snippet doesn't show how and where is glyph created, but your second snippet explicitly sets glyph to NULL.
You should assign created surface to glyph before using it in SDL_BlitSurface function.
Edit:
For rendering glyphs on surface, first create new surface, fill it with background color, and then blit glyph on it. You can use rectangle to define blit position if you want:
SDL_Surface * glyph = SDL_CreateRGBSurface(0, 100, 100, 32, 0, 0, 0, 0);
SDL_FillRect(glyph, NULL, SDL_MapRGB(glyph->format, 255, 255, 255);
ch = 66;
SDL_Surface * car = TTF_RenderGlyph_Blended(font, ch, color);
qDebug() << SDL_BlitSurface(car, NULL, glyph, NULL);
qDebug() << SDL_BlitSurface(glyph, NULL, screen, NULL);
Manual says you shouldn't call for locked surfaces when using SDL_BlitSurface(). Try to SDL_UnlockSurface() before call SDL_BlitSurface() for your surfaces. And for more information check what is the returned value of SDL_BlitSurface(). Before that you have to check for source surface to see whether it's filled or not, and try to use SDL_FillRect() on destination surface before blitting and see what happens.
Although, check for correct surface format:
http://wiki.libsdl.org/SDL_BlitSurface#Remarks
As MahamGM said, there was a format issue which is solved now :
Uint32 rmask, gmask, bmask, amask;
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
ch = 65;
SDL_Surface * glyph = SDL_CreateRGBSurface(0,screen->w,screen->h,32,rmask,gmask,bmask,amask);
SDL_Surface * car = TTF_RenderGlyph_Blended(font,ch,color);
SDL_Rect glyphRect = {0, 0, 100, 100};
SDL_Rect carRect = {100, 0, 300, 300};
PHDEBUG << SDL_BlitSurface(car, NULL, glyph, &glyphRect);
PHDEBUG << SDL_BlitSurface(glyph, NULL, screen, &glyphRect);
I'm working with SDL and OpenGL.
I'm using SDL ttf for create a surface with the text, then create a texture with that surface.
I'm trying to apply an alpha channel (opacity) to the text, I'm using this way:
SDL_Surface * fontsurf = TTF_RenderUTF8_Blended(font,buff_split.at(i).c_str(),color);
SDL_PixelFormat *format = fontsurf->format;
SDL_Surface* newSurface = SDL_CreateRGBSurface( SDL_SRCALPHA,
fontsurf->w, fontsurf->h, 32,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 );
Uint32 alpha = 0;
alpha = SDL_MapRGBA( newSurface->format, 0,0,0, color.unused);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.h = fontsurf->h;
rect.w = fontsurf->w;
int ret = SDL_FillRect( newSurface, &rect, alpha);
SDL_SetAlpha(newSurface,SDL_SRCALPHA,SDL_ALPHA_TRANSPARENT);
ret = SDL_BlitSurface( fontsurf, 0, newSurface, 0 );
The opacity is applied correctly, but the SDL_MapRGBA creates a black background in the "newSurface".
How can I apply alpha/opacity to the text, without adding a background, only the text?
I think you are missing SDL_SetColorKey for newSurface.
Something like this:
SDL_SetColorKey( newSurface , SDL_SRCCOLORKEY, SDL_MapRGB( newSurface->format, 0, 0, 0) );
or even easier would be to skip filling newSurface with a black rectangle
remove this:
SDL_FillRect( newSurface, &rect, alpha);
Here is an example how I make a picture with transparent color:
testImage = IMG_Load( "trans.png" );
// This picture contains color 255,0,255 which will be set as transparent
SDL_SetColorKey( testImage , SDL_SRCCOLORKEY, SDL_MapRGB( screen->format, 255, 0, 255) );
// I set the color 255,0,255 as transparent for that surface
SDL_BlitSurface( testImage , NULL , screen , NULL );
// and I draw it on the main surface (screen)