I am trying to render an image using SDL_image v2.0.0, in SDL2.
I have an image called Red.png in my res/img/ folder. When I try to load the texture, and use SDL_QueryTexture() it gets the size and everything just fine. But when it comes to rendering the actual image, Ive put a rectangle outline to know where the image is, but there is no image in the box.
The class I use to load and render the texture:
class LTexture
{
public:
~LTexture()
{
SDL_DestroyTexture(image_);
renderer_ = nullptr;
image_ = nullptr;
}
void init(SDL_Renderer* renderer)
{
printf("init texture\n");
renderer_ = renderer;
}
void loadBMP(std::string filename)
{
printf("load texture\n");
image_ = IMG_LoadTexture(renderer_, ("res/img/"+filename).c_str());
SDL_QueryTexture(image_, NULL, NULL, &imgrect.w, &imgrect.h);
}
void render(int x, int y)
{
imgrect.x = x;
imgrect.y = y;
SDL_SetRenderDrawColor(renderer_, 128, 128, 128, 255);
if (image_ != nullptr && renderer_ != nullptr)
{
printf("%i, %i\n", imgrect.x, imgrect.y);
SDL_RenderDrawRect(renderer_, &imgrect);
SDL_RenderCopy(renderer_, image_, &imgrect, &imgrect);
}
}
bool isLoaded()
{
return image_ != nullptr;
}
private:
SDL_Renderer* renderer_ = nullptr;
SDL_Texture* image_ = nullptr;
SDL_Rect imgrect;
};
I know it correctly gets the renderer and loads the image because the DrawRect function works, and if you didn't guess by the name, Red.png is a red rectangle.
Pass nullptr in for srcrect in your SDL_RenderCopy() call:
SDL_RenderCopy(renderer_, image_, nullptr, &imgrect);
Right now if x and/or y are larger than image_ SDL will clip srcrect to the extents of image_, end up with an empty rect, and do nothing.
Example:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <cstdlib>
#include <string>
class LTexture
{
public:
LTexture()
: renderer_( nullptr )
, image_( nullptr )
{ }
~LTexture()
{
SDL_DestroyTexture(image_);
renderer_ = nullptr;
image_ = nullptr;
}
void init(SDL_Renderer* renderer)
{
printf("init texture\n");
renderer_ = renderer;
}
void loadBMP(std::string filename)
{
printf("load texture\n");
image_ = IMG_LoadTexture(renderer_, filename.c_str());
SDL_QueryTexture(image_, NULL, NULL, &imgrect.w, &imgrect.h);
}
void render(int x, int y)
{
imgrect.x = x;
imgrect.y = y;
SDL_SetRenderDrawColor(renderer_, 128, 128, 128, 255);
if (image_ != nullptr && renderer_ != nullptr)
{
printf("%i, %i\n", imgrect.x, imgrect.y);
SDL_RenderDrawRect(renderer_, &imgrect);
SDL_RenderCopy(renderer_, image_, nullptr, &imgrect);
}
}
bool isLoaded()
{
return image_ != nullptr;
}
private:
SDL_Renderer* renderer_;
SDL_Texture* image_;
SDL_Rect imgrect;
};
int main( int argc, char** argv )
{
SDL_Init( SDL_INIT_EVERYTHING );
IMG_Init( IMG_INIT_PNG );
SDL_Window * window = SDL_CreateWindow
(
"SDL2",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
300, 300,
SDL_WINDOW_SHOWN
);
SDL_Renderer* renderer = SDL_CreateRenderer
(
window,
0,
SDL_RENDERER_ACCELERATED
);
LTexture tex;
tex.init( renderer );
tex.loadBMP( "red.png" );
bool running = true;
while( running )
{
SDL_Event ev;
while( SDL_PollEvent( &ev ) )
{
if ( ev.type == SDL_QUIT )
running = false;
}
SDL_SetRenderDrawColor( renderer, 0, 0, 0, 255 );
SDL_RenderFillRect( renderer, NULL );
tex.render( 50, 50 );
SDL_RenderPresent( renderer );
SDL_Delay( 33 );
}
SDL_DestroyRenderer( renderer );
SDL_DestroyWindow( window );
IMG_Quit();
SDL_Quit();
return 0;
}
red.png for reference:
I don't know if this is still actual, but I have problems using SDL_RENDERER_ACCELERATED flag (under Linuix Mint), when I use SW acceleration, SDL_Render* works.
Related
I've been following LazyFoo's SDL tutorials (and also adding my own organization and coding style). When I got to his animation tutorial I decided to make a separate class to store the variables and methods related to the animation algorithm, rather than having global variables. He uses an array of SDL_Rects to define the boundaries of different sprites on a sprite sheet, so I used an SDL_Rect pointer to store the array in my custom class. When I compiled everything I didn't see an animation, when I compiled the original source code I did. When I started debugging things, I found that when I was rendering the sprites, the rects were actually full of garbage, even though when I initialize them the rects are just fine. I've tried to simplify the problem so many times, but every approach I take to recreate the bug in a simpler environment actually works as expected! So with that in mind I apologize for the large amount of code, because I can't seem to reduce the problem.
texture.h
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL2/SDL.h>
#include <string>
class Animation {
public:
Animation(SDL_Renderer* renderer);
~Animation();
void load(std::string path, int frames, SDL_Rect* clips),
free(),
render(int x, int y),
next_frame();
private:
SDL_Renderer* _renderer=NULL;
SDL_Rect* _clips=NULL;
SDL_Texture* _texture=NULL;
int _frame=0, _frames=0, _width=0, _height=0;
};
#endif
texture.cpp
#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"
Animation::Animation(SDL_Renderer* renderer) {
_renderer = renderer;
}
Animation::~Animation() {
free();
_renderer = NULL;
}
void Animation::load(std::string path, int frames, SDL_Rect* clips) {
free();
SDL_Texture* texture = NULL;
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface)
throw ErrorIMG("Could not load image "+path);
SDL_SetColorKey(surface, SDL_TRUE,
SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));
texture = SDL_CreateTextureFromSurface(_renderer, surface);
if (!texture)
throw ErrorSDL("Could not create texture from image "+path);
_width = surface->w;
_height = surface->h;
SDL_FreeSurface(surface);
_frames = frames;
_clips = clips;
printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}
void Animation::free() {
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = NULL;
_clips = NULL;
_frames = 0;
_frame = 0;
_width = 0;
_height = 0;
}
}
void Animation::render(int x, int y) {
SDL_Rect crect = _clips[_frame/4];
printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
SDL_Rect render_space = {x, y, crect.w, crect.h};
SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}
void Animation::next_frame() {
SDL_Rect crect = _clips[_frame/4];
printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
++_frame;
if (_frame/4 >= _frames)
_frame = 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include "texture.h"
class Game {
public:
Game();
~Game();
void main();
private:
void load_media();
SDL_Window* _window=NULL;
SDL_Renderer* _renderer=NULL;
Animation* _anim=NULL;
const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};
#endif
game.cpp
#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"
void Game::main() {
load_media();
bool has_quit = false;
SDL_Event event;
while (!has_quit) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
has_quit = true;
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(_renderer);
_anim->render(100, 100);
_anim->next_frame();
SDL_RenderPresent(_renderer);
}
}
Game::Game() {
if (SDL_Init(SDL_INIT_VIDEO))
throw ErrorSDL("SDL could not initialize");
_window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!_window)
throw ErrorSDL("Window could not be created");
Uint32 render_flags = SDL_RENDERER_ACCELERATED;
render_flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, render_flags);
if (!_renderer)
throw ErrorSDL("Renderer could not be created");
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
throw ErrorIMG("SDL_image could not initialize");
}
Game::~Game() {
delete _anim;
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_renderer = NULL;
_window = NULL;
IMG_Quit();
SDL_Quit();
}
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
for (int i=0; i < nclips; i++) {
clips[i].x = i*64;
clips[i].y = 0;
clips[i].w = 64;
clips[i].h = 164;
}
_anim = new Animation(_renderer);
_anim->load("sheet.png", nclips, &clips[0]);
}
You're storing a pointer to a temporary. The SDL_Rect* clips pointer that you pass to Animation::load is assigned to the member _clips used after the function returns. For this to work correctly, the data that is pointed to needs to live for as long as the Animation class is using it. The problem arises here:
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
...
_anim->load("sheet.png", nclips, &clips[0]);
}
In this piece of code, clips is a local variable. That means it gets destroyed at the end of load_media(), and the memory contents at that location will become garbage.
There are a number of ways you could fix this. A simple one would be to use std::vector<SDL_Rect> instead of SDL_Rect*. std::vector can safely be copied and manages its internals for you. Your new code could look like:
class Animation {
...
std::vector<SDL_Rect> _clips;
...
}
void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
...
_clips = clips;
...
}
void Game::load_media() {
const int nclips = 4;
std::vector<SDL_Rect> clips;
clips.resize(nclips);
...
_anim->load("sheet.png", nclips, clips);
}
And dont forget to #include <vector>. Documentation for std::vector is here. Note that std::vector has a size() method that can probably replace frames everywhere it appears.
The stack-allocated Game::load_media()::clips array disappears when it goes out of scope. Make a copy in Animation::load() instead of only storing a pointer.
I'm trying to load a image("carnero.png") but when I use IMG_LoadTexture(), it returns null;
Game.h
#ifndef GAME_H_
#define GAME_H_
#include <SDL.h>
#include <SDL_image.h>
#include <windows.h>
class Game {
public:
Game();
~Game();
void run();
void initGraphics();
void gameLoop();
private:
SDL_Window* _window = nullptr;
SDL_Renderer* _renderer;
SDL_Surface* _surfaceBMP;
SDL_Texture* _textureScenario;
SDL_Texture* _textureCarnero;
SDL_Rect* _scenarioRect;
SDL_Rect* _carneroRect;
int _width;
int _height;
bool _running;
};
#endif /* SRC_GAME_H_ */
Game.cpp
#include "Game.h"
#include <iostream>
Game::Game(){
_running = true;
run();
}
Game::~Game(){
}
void Game::run(){
initGraphics();
gameLoop();
}
void Game::initGraphics(){
SDL_Init(SDL_INIT_VIDEO);
IMG_Init(IMG_INIT_PNG);
_window = SDL_CreateWindow("Carneiro", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1024, 768, SDL_WINDOW_SHOWN);
if(_window == nullptr) exit(1);
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
_surfaceBMP = SDL_LoadBMP("textures/scenario.bmp");
_textureScenario = SDL_CreateTextureFromSurface(_renderer, _surfaceBMP);
SDL_FreeSurface(_surfaceBMP);
_textureCarnero = IMG_LoadTexture(_renderer, "/textures/carnero2.png");
if(_textureCarnero == nullptr) exit(1);
_scenarioRect->x = 0; _scenarioRect->w = 1024;
_scenarioRect->y = 0; _scenarioRect->h = 740;
_carneroRect->x = 20; _carneroRect->w = 150;
_carneroRect->y = 100; _carneroRect->h = 100;
}
void Game::gameLoop(){
while(_running){
Sleep(10);
SDL_Event evnt;
if(SDL_PollEvent(&evnt)){
switch(evnt.type){
case SDL_QUIT:
_running = false;
break;
}
}
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _textureScenario, nullptr, _scenarioRect);
// SDL_QueryTexture(_textureCarnero, NULL, NULL, &_carneroRect->x, &_carneroRect->y);
SDL_RenderCopy(_renderer, _textureCarnero, nullptr, _carneroRect);
SDL_RenderPresent(_renderer);
}
SDL_DestroyTexture(_textureScenario);
SDL_DestroyTexture(_textureCarnero);
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
SDL_Quit();
IMG_Quit();
}
This function returns null
_textureCarnero = IMG_LoadTexture(_renderer, "/textures/carnero2.png");
But when I use SDL_LoadBMP() to load the background it works. I tried putting my .png in other folders but it doesn't work either. I also tried to load my .png using IMG_LOAD() but i had no sucess.
Your path is incorrect. /textures/carnero2.png will search for a file in C:\textures\carnero2.png, or /textures/carnero2.png on unix.
You can solve this problem as follows:
Use full (absolute) path: C:\Program Files (x86)\MyGame\textures\carnero2.png, /usr/local/share/mygame/textures/carnero2.png
Add a dot ./textures/carnero2.png
Remove the slash: textures/carnero2.png.
The path to your input file is probably incorrect:
/textures/carnero2.png
should probably be
textures/carnero2.png
like in the previous (working) load command.
In the future, I suggest that you test for file existence before trying to load the file. So you can separate "file not found" errors from real format/corrupt file problems.
I was following Lazy Foo' tutorial to create text using ttf font, and everything was fine, but I needed to create several text lines in several different places with different font size and color, so I decided to use vector. Here is my code of TextTexture (mostly copy of Lazy Foo tutorial):
#ifndef TEXT_TEXTURE_HPP
#define TEXT_TEXTURE_HPP
#include "graphics.hpp"
#include "vector2.hpp"
#include <SDL2/SDL_ttf.h>
#include <string>
class TextTexture {
public:
TextTexture(
Graphics& graphics,
TTF_Font* font,
std::string textureText,
SDL_Color textColor,
Vector2 coordinates
);
~TextTexture();
void draw( Graphics& graphics );
private:
SDL_Texture* mTexture;
int mWidth;
int mHeight;
int mX;
int mY;
};
#endif // TEXT_TEXTURE_HPP
And .cpp file for it:
#include "text_texture.hpp"
#include "vector2.hpp"
#include <iostream>
#include <unistd.h>
TextTexture::TextTexture (
Graphics& graphics,
TTF_Font* font,
std::string textureText,
SDL_Color textColor,
Vector2 coordinates
) :
mTexture(NULL),
mWidth(0),
mHeight(0),
mX(0),
mY(0)
{
//Render temp surface
SDL_Surface* tempSurface = TTF_RenderUTF8_Blended (font, textureText.c_str(), textColor);
if ( tempSurface == NULL ) {
std::cout << "Unable to render text surface! SDL_ttf Error: " << TTF_GetError() << std::endl;
} else {
this -> mTexture = SDL_CreateTextureFromSurface(graphics.getRenderer(), tempSurface);
if ( this -> mTexture == NULL ) {
std::cout << "Unable to create texture from rendered text! SDL Error: " << SDL_GetError() << std::endl;
} else {
//Get image dimensions
mWidth = tempSurface -> w;
mHeight = tempSurface -> h;
// Get coordinates
this -> mX = coordinates.getX();
this -> mY = coordinates.getY();
}
SDL_FreeSurface (tempSurface);
tempSurface = NULL;
}
}
TextTexture::~TextTexture() {
//Free texture if it exists
if ( mTexture != NULL ) {
SDL_DestroyTexture( mTexture );
}
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
// FIXME somewhy affects previous dest rects
void TextTexture::draw (Graphics& graphics) {
//Set rendering space and render to screen
SDL_Rect destinationRectangle = { mX, mY, this -> mWidth, this -> mHeight };
//Render to screen
graphics.blitSurface( mTexture, NULL, &destinationRectangle );
}
I created simple Text Manager to handle vector of texts:
#ifndef TEXT_MANAGER_HPP
#define TEXT_MANAGER_HPP
#include "graphics.hpp"
#include "text_texture.hpp"
#include "vector2.hpp"
#include <string>
#include <vector>
enum fontSize {
SMALL = 16,
NORMAL = 32,
BIG = 48,
TITLE = 72
};
enum fontColor {
WHITE,
ORANGE,
BLACK
};
class TextManager {
public:
TextManager(Graphics& graphics);
~TextManager();
void addText(std::string, fontSize, fontColor, Vector2);
void draw();
void clearText();
private:
Graphics& graphics;
std::vector <TextTexture> gText;
};
#endif // TEXT_MANAGER_HPP
and .cpp file:
#include "text_manager.hpp"
#include <iostream>
TextManager::TextManager(Graphics& graphics) :
graphics(graphics)
{}
TextManager::~TextManager() {}
void TextManager::addText(std::string text, fontSize size, fontColor color, Vector2 coordinates) {
TTF_Font* tempFont = TTF_OpenFont( "resources/fonts/pixel.ttf", fontSize::TITLE );
SDL_Color tempColor = { 255, 255, 255 };
// Switch removed for shorter code
this -> gText.emplace_back(graphics, tempFont, text, tempColor, coordinates);
TTF_CloseFont(tempFont);
tempFont = NULL;
}
// FIXME
void TextManager::draw() {
std::vector<TextTexture>::iterator it;
for(it = gText.begin(); it != gText.end(); ++it) {
it -> draw(graphics);
}
}
void TextManager::clearText() {
gText.clear();
}
But when I start the application, I see something like this:
Second string is printed, but font and bonding rectangle of first line is saved, hovewer
Later I added input handler that added second line of text after pressing a button, and when there is only one line of text, everything fine, but when you add second, something weird is beginning - sometimes first text disappears, sometimes 'both' of them is shoved. As I understand, second surface of text somehow affects first one, by copying it texture on the place of the first's destination.
Here is my graphics.blitSurface, if it will help:
void Graphics::blitSurface(SDL_Texture* texture, SDL_Rect* sourceRectangle, SDL_Rect* destinationRectangle)
{
SDL_RenderCopy ( this -> _renderer, texture, sourceRectangle, destinationRectangle );
}
Where is my mistake? Sorry for bad english, I hope you will get my problem.
I figured it out somehow randomly. The thing is that when I adding object to vector, it's calls a destructor.
Here is why:
Why does my class's destructor get called when I add instances to a vector?
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am having a lot of trouble with my header files and the compilation. I have everything linked with header guards but for some reason I am still getting a lot of multiple definition errors. I'm also look for help on better way to organize code. Whatever help is appreciated!
This is the out output of my console when I do the g++ call:
g++ main.cpp close.cpp init.cpp load_media.cpp texture.cpp -w -lSDL2 -lSDL2_image -lSDL2_mixer -lSDL2_ttf -o run
/tmp/cc3oNgPs.o:(.bss+0x0): multiple definition of `g_font'
/tmp/ccg0hCKW.o:(.bss+0x0): first defined here
/tmp/cc3oNgPs.o:(.bss+0x8): multiple definition of `g_window'
/tmp/ccg0hCKW.o:(.bss+0x8): first defined here
/tmp/cc3oNgPs.o:(.bss+0x10): multiple definition of `g_renderer'
/tmp/ccg0hCKW.o:(.bss+0x10): first defined here
/tmp/cc3oNgPs.o:(.bss+0x20): multiple definition of `g_text_texture'
/tmp/ccg0hCKW.o:(.bss+0x20): first defined here
/tmp/ccIgzhbZ.o:(.bss+0x0): multiple definition of `g_font'
/tmp/ccg0hCKW.o:(.bss+0x0): first defined here
/tmp/ccIgzhbZ.o:(.bss+0x8): multiple definition of `g_window'
/tmp/ccg0hCKW.o:(.bss+0x8): first defined here
/tmp/ccIgzhbZ.o:(.bss+0x10): multiple definition of `g_renderer'
/tmp/ccg0hCKW.o:(.bss+0x10): first defined here
/tmp/ccIgzhbZ.o:(.bss+0x20): multiple definition of `g_text_texture'
/tmp/ccg0hCKW.o:(.bss+0x20): first defined here
/tmp/ccQs9gPv.o:(.bss+0x0): multiple definition of `g_font'
/tmp/ccg0hCKW.o:(.bss+0x0): first defined here
/tmp/ccQs9gPv.o:(.bss+0x8): multiple definition of `g_window'
/tmp/ccg0hCKW.o:(.bss+0x8): first defined here
/tmp/ccQs9gPv.o:(.bss+0x10): multiple definition of `g_renderer'
/tmp/ccg0hCKW.o:(.bss+0x10): first defined here
/tmp/ccQs9gPv.o:(.bss+0x20): multiple definition of `g_text_texture'
/tmp/ccg0hCKW.o:(.bss+0x20): first defined here
/tmp/ccxzUgM2.o:(.bss+0x0): multiple definition of `g_font'
/tmp/ccg0hCKW.o:(.bss+0x0): first defined here
/tmp/ccxzUgM2.o:(.bss+0x8): multiple definition of `g_window'
/tmp/ccg0hCKW.o:(.bss+0x8): first defined here
/tmp/ccxzUgM2.o:(.bss+0x10): multiple definition of `g_renderer'
/tmp/ccg0hCKW.o:(.bss+0x10): first defined here
/tmp/ccxzUgM2.o:(.bss+0x20): multiple definition of `g_text_texture'
/tmp/ccg0hCKW.o:(.bss+0x20): first defined here
collect2: error: ld returned 1 exit status
here are my 2 header files:
isolation.h
//include //include guard
#ifndef ISOLATION_H
#define ISOLATION_H
//include dependencies
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string>
//include headers
#include "texture.h"
//Screen dimension constants
const int SCREEN_WIDTH = 1280;
const int SCREEN_HEIGHT = 800;
//forward delcarlation
//class Texture;
//start up SDL create window
bool init();
//load all media
bool load_media();
//free all and shut down SDL
void close();
//load global front
TTF_Font* g_font = NULL;
//window
SDL_Window* g_window = NULL;
//renderer
SDL_Renderer* g_renderer = NULL;
//load jpeg + font
//Texture background_texture;
//rendered font texture
Texture g_text_texture;
#endif
texture.h
//include guard
#ifndef TEXTURE_H
#define TEXTURE_H
//include dependencies
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string>
//include headers
//#include "isolation.h"
class Texture {
public:
//initializes variables
Texture();
//deallocates memory
~Texture();
//load image from path
bool load_from_file( std::string path );
//create image from font string
bool load_from_rendered_text( std::string textureText, SDL_Color text_color );
//deallocates texture
void free();
//set color modulation
void set_color( Uint8 red, Uint8 green, Uint8 blue );
//set blend mode
void set_blend_mode( SDL_BlendMode blending );
//set alpha
void set_alpha( Uint8 alpha );
//render texture at point
void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE ) const;
//get image dimensions
int get_width() const;
int get_height() const;
private:
//texture pointer
SDL_Texture* m_texture;
//dimensions
int m_width;
int m_height;
};
#endif
init.cpp
#include "isolation.h"
//#include "texture.h"
bool init() {
//initialization flag
bool success = true;
//initialize SDL
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
printf( "SDL could not initialized. SDL Error: %s\n", SDL_GetError() );
success = false;
}
else {
//set texture filtering linear
if ( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) ) {
printf( "Warning: Linear filtering not enabled\n" );
}
//create window
g_window = SDL_CreateWindow( "Isolation", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if ( g_window == NULL ) {
printf( "Window could not be created. SDL Error: %s\n", SDL_GetError() );
success = false;
}
else {
//create vsynced renderer
g_renderer = SDL_CreateRenderer( g_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
if ( g_renderer == NULL ) {
printf( "Renderer could not be created. SDL Error: %s\n", SDL_GetError() );
success = false;
}
else {
//initialize renderer color
SDL_SetRenderDrawColor (g_renderer, 0xFF, 0xFF, 0xFF, 0xFF );
//initialize JPEG loading
int img_flags = IMG_INIT_JPG;
if ( !( IMG_Init( img_flags ) & img_flags ) ) {
printf( "SDL_image could not be initialize. SDL_image Error: %s\n", IMG_GetError() );
success = false;
}
//initialize SDL_ttf
if (TTF_Init() == -1 ) {
printf( "SDL_ttf could not be initialize. SDL_ttf Error: %s\n", TTF_GetError() );
}
}
}
}
return success;
}
load_media.cpp
#include "isolation.h"
//s#include "texture.h"
bool load_media() {
bool success = true;
//load background img
//if( !background_texture.load_from_file( "img/vancouver.jpg" ) ) {
// printf( "Failed to load background texture!\n" );
// success = false;
//}
//open font
g_font = TTF_OpenFont( "lazy.ttf", 28 );
if ( g_font == NULL ) {
printf( "Failed to load lazy font. SDL_ttf Error: %s\n", TTF_GetError() );
success = false;
}
else {
//render texture
SDL_Color text_color = { 0, 0, 0 };
if ( !g_text_texture.load_from_rendered_text( "hello from the other side ", text_color ) ) {
printf( "Failed to render text texture\n" );
success = false;
}
}
return success;
}
close.cpp
#include "isolation.h"
//#include "texture.h"
void close() {
//free loaded text
g_text_texture.free();
//free font
TTF_CloseFont( g_font );
g_font = NULL;
//destroy window
SDL_DestroyWindow( g_window );
SDL_DestroyRenderer( g_renderer );
g_window = NULL;
g_renderer = NULL;
//quit SDL subsystems
TTF_Quit();
IMG_Quit();
SDL_Quit();
}
main.cpp
#include "isolation.h"
//#include "texture.h"
int main( int argc, char* args[] ) {
//start up SDL
if ( !init() ) {
printf( "Failed to initialize.\n" );
}
else {
//load media
if ( !load_media() ) {
printf( "Failed to load media.\n" );
}
else{
//main loop flag
bool quit = false;
//event handler
SDL_Event e;
//while running
while ( !quit ) {
//handle events on queue
while ( SDL_PollEvent( &e ) != 0 ) {
//user quit
if ( e.type == SDL_QUIT ) {
quit = true;
}
}
//clear screen
SDL_SetRenderDrawColor( g_renderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( g_renderer );
//render frame
g_text_texture.render( ( SCREEN_WIDTH - g_text_texture.get_width() ) / 2, ( SCREEN_HEIGHT - g_text_texture.get_height() ) / 2 );
//update screen
SDL_RenderPresent( g_renderer );
}
}
}
//free memory and close SDL
close();
return 0;
}
Do not mix declaration with definition/instantiation in a .h file. Your are instantiating g_font, g_window and g_renderer in isolation.h file. The correct is instantiating only once, usually, in a .cpp
To solve your problem, change isolation.h to declare those variables as external linkage:
//load global front
extern TTF_Font* g_font;
//window
extern SDL_Window* g_window;
//renderer
extern SDL_Renderer* g_renderer;
and instantiate them only once in an appropriate .cpp file, for example, init.cpp
(See "Edit 2" below for the solution.)
I need to create SDL surfaces from scratch, instead of loading them from a file. Unfortunately, SDL_BlitSurface() seems to render all colors as black when used with the surface generated through SDL_CreateRGBSurface(). This is my code:
int main(int argc, char** argv)
{
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
SDL_Surface* layer = SDL_CreateRGBSurface(SDL_HWSURFACE, 100, 100,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask
);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = 100;
rect.h = 100;
Uint32 blue = SDL_MapRGB(screen->format, 0, 0, 255);
SDL_FillRect(layer, &rect, blue);
SDL_BlitSurface(screen, NULL, layer, NULL);
SDL_Flip(screen);
SDL_Delay(3000);
return 0;
}
What I get is a black screen, instead of a 100x100 blue rectangle. What I could find by Googling doesn't seem to help me, as those questions either apply to 8bit surfaces (and setting palettes — my bpp is 32 here) or are left unanswered.
So, I would like to know how should I properly blit a generated surface onto a SDL screen.
Edit: I see it was an error in the parameter ordering. The line in question should read
SDL_BlitSurface(layer, NULL, screen, NULL);
Still, I am having trouble to achieve the same effect in my more complex C++ program. I will post the relevant parts of the code here:
main.cpp:
int main(int argc, char** argv)
{
SDLScreen screen(1024, 700, "Hello, SDL!");
SDL_Event event;
SDLMenu menu;
bool shouldQuit = false;
menu.setBounds(200, 100, 200, 600);
menu.setFontName("NK211.otf");
menu.setFontSize(36);
menu.setEffect(sdlteShadowText);
menu.addItem("New game");
menu.addItem("Load game");
menu.addItem("Save game");
menu.addItem("Exit");
menu.render();
while (!shouldQuit)
{
menu.draw(screen.getSurface());
SDL_Flip(screen.getSurface());
SDL_Delay(10);
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
shouldQuit = true;
}
else if (event.type == SDL_KEYUP)
{
if (event.key.keysym.sym == SDLK_q)
{
shouldQuit = true;
}
}
}
}
}
SDLMenu.cpp:
void
SDLMenu::setSelectionColorRGB(int r, int g, int b)
{
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
selectionColor = SDL_MapRGB(info->vfmt, r, g, b);
}
void
SDLMenu::render()
{
SDLText* current = NULL;
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
if (!items->empty())
{
current = getItemAt(currentItem);
selectionRect = getItemRect(current);
setSelectionColorRGB(0,0,255);
selectionCanvas = SDL_CreateRGBSurface(SDL_HWSURFACE,
selectionRect->w, selectionRect->h,
info->vfmt->BitsPerPixel,
info->vfmt->Rmask,
info->vfmt->Gmask,
info->vfmt->Bmask,
info->vfmt->Amask);
SDL_FillRect(selectionCanvas, selectionRect, selectionColor);
SDL_SaveBMP(selectionCanvas, "selection.bmp"); // debug
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->render();
}
}
void
SDLMenu::draw(SDL_Surface* canvas)
{
int currentY = bounds.y;
if (selectionCanvas != NULL)
{
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->draw(bounds.x, currentY, canvas);
currentY += fontSize + itemGap;
}
}
SDLScreen.cpp:
SDLScreen::SDLScreen(int w, int h, string t, int d)
: width(w), height(h), depth(d), title(t)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_WM_SetCaption(title.c_str(), NULL);
refresh();
}
void
SDLScreen::refresh()
{
screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE);
}
The selection rectangle for the active menu item should be blue, but it shows up in black. The file selection.bmp is also all black.
Edit 2: I found out what created the problem. The selectionRect was set relative to the screen, while the selectionCanvas had the width and height of a particular menu item. So, the filling was done out of bounds of the selectionCanvas. Adding separate SDL_Rect for filling solved the problem.
SDL_Rect fillRect;
fillRect.x = 0;
fillRect.y = 0;
fillRect.w = selectionRect->w;
fillRect.h = selectionRect->h;
SDL_FillRect(selectionCanvas, &fillRect, selectionColor);
// and later...
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
You inverted source and destination. To blit on screen, it should be
SDL_BlitSurface(layer, NULL, screen, NULL);
doc for SDL_BlitSurface