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);
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.
In order to make buttons, I create and render texture that way:
typedef struct{
SDL_Rect pos;
SDL_Texture* texture;
int hovered;
} button;
button getButton(int x, int y, char * label, TTF_Font* font, SDL_Color color){
button btn;
btn.hovered = false;
btn.pos.x = x;
btn.pos.y = y;
SDL_Surface* surface = TTF_RenderText_Solid(font, label, color);
btn.pos.w = surface->w;
btn.pos.h = surface->h;
btn.texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return btn;
}
void drawButton(button btn){
SDL_RenderCopyEx( renderer, btn.texture, NULL, &btn.pos, 0, NULL, SDL_FLIP_NONE);
if(btn.hovered){
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0x00);
SDL_RenderDrawRect(renderer, &btn.pos);
}
The problem is that I get texture which size equals one of label. How can I increase texture pixel size without stretching it i.e. add blank spaces to the side of it?
Something like
void drawButton(button btn){
SDL_RenderCopyEx( renderer, btn.texture, NULL, &btn.pos, 0, NULL, SDL_FLIP_NONE);
if(btn.hovered){
int padding = 10;
SDL_Rect pos = {btn.pos.x - padding, btn.pos.y - padding,
btn.pos.w + 2*padding, btn.pos.h + 2*padding };
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0x00);
SDL_RenderDrawRect(renderer, &pos);
}
}
That way only the size of the rectangle changes, obviously I just pulled 10 for the size of padding out of thin air, you'll want to pick something appropriate yourself.
Found a way to do it. To enlarge the texture you create surface representing background of button, then combine them:
button getButton(int x, int y, char * label, TTF_Font* font, SDL_Color color){
button btn;
btn.hovered = false;
btn.pos.x = x;
btn.pos.y = y;
SDL_Surface* surface = TTF_RenderText_Solid(font, label, color);
SDL_Surface* back = SDL_CreateRGBSurface(0, surface->w+10, surface->h+10,
32, 0, 0, 0, 0);// create a black background
SDL_Rect t = {5, 5, back->w, back->w}; // place in a background to place label
SDL_BlitSurface(surface, NULL, back, &t); // combining surfaces
btn.pos.w = back->w;
btn.pos.h = back->h;
btn.texture = SDL_CreateTextureFromSurface(renderer, back);
SDL_FreeSurface(surface);
return btn;
}
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);
Here's my code, using SDL 2.0.4 on OSX 10.11.4:
SDL_Surface *output_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
SDL_Texture *output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_Color c[256];
// Setting each color to red as a test.
for(u8 i = 255; i--;) {
c[i].r = 255;
c[i].g = 0;
c[i].b = 0;
}
SDL_SetPaletteColors(output_surface->format->palette, c, 0, 256);
Then later...
SDL_Rect r = {
.x = 0,
.y = 0,
.w = width,
.h = height
};
// Doesn't fill with red.
SDL_FillRect(output_surface, &r, 4);
SDL_UpdateTexture(output_texture, NULL, output_surface->pixels, output_surface->pitch);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, output_texture, NULL, NULL);
SDL_RenderPresent(renderer);
What I would expect to see is the full window all red but I'm getting something entirely different. Changing the color number passed to SDL_FillRect shows that I'm getting a grayscale palette (0 is black, 255 is white) even though SDL_SetPaletteColors doesn't return an error and i've looped through output_surface->format->palette->colors to verify the palette's been changed.
What am I missing here?
edit: I was asked to post an entire program. Here it is:
int main(int argc, const char *argv[]) {
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_Surface *output_surface = NULL;
SDL_Texture *output_texture = NULL;
int width = 640;
int height = 480;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) return 0;
window = SDL_CreateWindow("Sample", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
output_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_Color c[256];
for(u8 i = 255; i--;) {
c[i].r = 255;
c[i].g = 0;
c[i].b = 0;
c[i].a = 255;
}
SDL_SetPaletteColors(output_surface->format->palette, c, 0, 255);
SDL_Rect r = {
.x = 0,
.y = 0,
.w = width,
.h = height
};
bool running = true;
while(running) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
switch(event.type){
case SDL_KEYDOWN:
running = false;
break;
}
}
SDL_FillRect(output_surface, &r, 124);
SDL_UpdateTexture(output_texture, NULL, output_surface->pixels, output_surface->pitch);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, output_texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_FreeSurface(output_surface);
SDL_DestroyTexture(output_texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Passing 0 to SDL_FillRect is black, 255 is white, and any number in-between is a shade of grey.
Alright, found the solution.
Remove this line:
output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
And instead add this line somewhere after the call to SDL_SetPaletteColors or after you change the surfaces' pixels (like in the game loop):
output_texture = SDL_CreateTextureFromSurface(renderer, output_surface);
So, after fooling around with the width and height numbers on the SDL_CreateRGBSurface() function, i am really confused on how they work. According to SDL Wiki the width and height refer to the width and height of the surface, however when I say SCREENWIDTH / 2, or SCREENHEIGHT / 2, it shows bigger than without the division. This is the code I was messing around with:
#include <iostream>
#include <SDL.h>
const int WIN_WIDTH = 640;
const int WIN_HEIGHT = 480;
int main(int argc, char **argv){
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n";
return 1;
}
SDL_Window *win = SDL_CreateWindow("Rendering to a texture!", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WIN_WIDTH, WIN_HEIGHT, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(win, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
SDL_Surface* s;
SDL_Color c = { 155, 0, 0 };
SDL_Rect r = { 0, 0, 100, 100 };
s = SDL_CreateRGBSurface(0, WIN_WIDTH / 2, WIN_HEIGHT, 32, 0, 0, 0, 0);
SDL_FillRect(s, &r, SDL_MapRGB(s->format, c.r, c.g, c.b));
SDL_Texture* t;
t = SDL_CreateTextureFromSurface(renderer, s);
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, t, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_Delay(2000);
SDL_FreeSurface(s);
SDL_DestroyTexture(t);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
All I really want is a rect with a width of 100 and height of 100, but to have it appear correctly on the screen, the width and height must be specified to the width and height of the window. Why is that?
SDL_RenderCopy will stretch the source texture to fit into the destination renderer. So it will stretch your surface over the entire window. If you want to draw a 100x100 pixel rectangle, your SDL surface should be the same size as the window you're copying it to; that way no stretching will take place and the surface will be presented 1:1.