SDL2 libSDL2_image.so: undefined reference to - c

I'm using Kubuntu 18.04
So i recently tried to use SDL2, i wanted to create a simple window showing a few pictures following a tutorial but i was unable to compile the example code.
When i try to compile the code i get:
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_ceilf'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_acosf'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_floorf'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_fabsf'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_LoadFile_RW'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_fmodf'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libSDL2_image.so: undefined reference to `SDL_atan2f'
collect2: error: ld returned 1 exit status
Command used:
gcc SDLProject.c -o SDLProject `sdl2-config --cflags --libs` -lSDL2_gfx -lSDL2_image
Command used to install SDL2 libs:
sudo apt install libsdl2-dev libsdl2-gfx-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev
Installed packages:
sudo apt list --installed | grep "sdl"
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
libsdl2-2.0-0/bionic-updates,now 2.0.8+dfsg1-1ubuntu1.18.04.3 amd64 [installed]
libsdl2-dev/bionic-updates,now 2.0.8+dfsg1-1ubuntu1.18.04.3 amd64 [installed]
libsdl2-gfx-1.0-0/bionic,now 1.0.4+dfsg-1 amd64 [installed,automatic]
libsdl2-gfx-dev/bionic,now 1.0.4+dfsg-1 amd64 [installed]
libsdl2-image-2.0-0/bionic,now 2.0.3+dfsg1-1 amd64 [installed,automatic]
libsdl2-image-dev/bionic,now 2.0.3+dfsg1-1 amd64 [installed]
libsdl2-mixer-2.0-0/bionic,now 2.0.2+dfsg1-2 amd64 [installed,automatic]
libsdl2-mixer-dev/bionic,now 2.0.2+dfsg1-2 amd64 [installed]
libsdl2-ttf-2.0-0/bionic,now 2.0.14+dfsg1-2 amd64 [installed,automatic]
libsdl2-ttf-dev/bionic,now 2.0.14+dfsg1-2 amd64 [installed]
I already tried deleting everything that is related to SDL / SDL2 and only reinstalling the ones the project uses (SDL2, gfx and image) and i found out that this error only happens when i try to inlude the SDL_image.h and I'm able to run other exmaple codes that does not use it.
I do not think that the error is related to the code itself but:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include <stdlib.h>
typedef enum Babu {
VKiraly, VVezer, VBastya, VFuto, VHuszar, VGyalog,
SKiraly, SVezer, SSastya, SFuto, SHuszar, SGyalog
} Babu;
enum { MERET = 52 };
void babu_rajzol(SDL_Renderer *renderer, SDL_Texture *babuk, Babu melyik, int x, int y) {
SDL_Rect src = { (melyik % 6) * 62 + 10, (melyik / 6) * 60 + 10, MERET, MERET };
SDL_Rect dest = { x, y, MERET, MERET };
SDL_RenderCopy(renderer, babuk, &src, &dest);
}
void sdl_init(int szeles, int magas, SDL_Window **pwindow, SDL_Renderer **prenderer) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
SDL_Log("Nem indithato az SDL: %s", SDL_GetError());
exit(1);
}
SDL_Window *window = SDL_CreateWindow("SDL peldaprogram", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, szeles, magas, 0);
if (window == NULL) {
SDL_Log("Nem hozhato letre az ablak: %s", SDL_GetError());
exit(1);
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
if (renderer == NULL) {
SDL_Log("Nem hozhato letre a megjelenito: %s", SDL_GetError());
exit(1);
}
SDL_RenderClear(renderer);
*pwindow = window;
*prenderer = renderer;
}
int main(int argc, char *argv[]) {
SDL_Window *window;
SDL_Renderer *renderer;
sdl_init(320, 200, &window, &renderer);
SDL_Texture *babuk = IMG_LoadTexture(renderer, "pieces.png");
if (babuk == NULL) {
SDL_Log("Nem nyithato meg a kepfajl: %s", IMG_GetError());
exit(1);
}
boxRGBA(renderer, 0, 0, 319, 199, 0x90, 0xE0, 0x90, 0xFF);
babu_rajzol(renderer, babuk, VKiraly, 82, 74);
babu_rajzol(renderer, babuk, SGyalog, 82+MERET, 74);
babu_rajzol(renderer, babuk, VHuszar, 82+2*MERET, 74);
SDL_RenderPresent(renderer);
SDL_DestroyTexture(babuk);
SDL_Event event;
while (SDL_WaitEvent(&event) && event.type != SDL_QUIT) {
}
SDL_Quit();
return 0;
}

You did not get the sdl package which contains sdl-config. You can get it from
sudo apt install libsdl1.2-dev

Related

C SDL2 Error: No hardware accelerated renderers available

I'm trying to create a pong game with sdl2 in c. I'm able to make it work in a laptop running UBUNTU 20.04 (gtx 1650 mobile) with nvidia driver 470. But running it on the same setup in ubuntu 21.10 with nvidia 470 on a different laptop (rtx 3060 mobile) gives me this error.
Here's the code (stripped off any game logic to only part where it crashes).
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "SDL2/SDL.h"
const int SCREEN_WIDTH = 1200;
const int SCREEN_HEIGHT = 800;
int main(int argc, char** argv)
{
int sdl_init_status = SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER);
if (sdl_init_status != 0)
{
printf("Initialization failed. Error: %s \n", SDL_GetError());
return 1;
}
SDL_Window* window = SDL_CreateWindow("PONG", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_RESIZABLE);
if (!window)
{
printf("Window initialization error: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
puts("Window created");
// // create renderer
Uint32 rflags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, rflags);
if (!renderer)
{
printf("renderer initialization error: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
puts("Renderer created");
SDL_Surface* screen_surface = SDL_GetWindowSurface(window);
if (!screen_surface)
{
printf("surface initialization error: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
puts("Surface created");
SDL_Delay(1000);
// cleanup and quit
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
This is the output I get:
gcc -std=c99 -g3 -Wall -o1 -o main main.c `sdl2-config --libs --cflags` -lm -lSDL2_image
./main
Window created
Renderer created
surface initialization error: No hardware accelerated renderers available
make: *** [Makefile:10: test] Error 1
You have
Uint32 rflags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC;
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, rflags);
Your error states No hardware accelerated renderers available.
The documentation for SDL_CreateRenderer refers 4 flags you can use.
I was having this issue on Android devices and solved with replacing the flags in SDL_CreateRenderer with only SDL_RENDERER_SOFTWARE. So use
Uint32 rflags = SDL_RENDERER_SOFTWARE;
instead.

Why am I failing to initialize SDL_image?

This is an updated post. Someone asked me to do a minimal reproducible example, which did help a lot. This program opens a window, but does not display a PNG image as I would like it to do.
The compiler gives me no errors, however my error message for SDL_Init does.
What could be the reason that I am failing to initialize SDL_Image?
// std
#include <stdio.h>
// sdl
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
int main (int argc, char* argv[])
{
// ----- Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
fprintf(stderr, "SDL could not initialize\n");
return 1;
}
if (IMG_Init(IMG_INIT_PNG) != 0)
{
fprintf(stderr, "SDL_image could not initialize\n");
/********
I GET THIS ERROR
********/
}
// ----- Create window
SDL_Window* window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
800,
600,
0);
if (!window)
{
fprintf(stderr, "Error creating window.\n");
return 2;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Surface *image_surface = NULL;
image_surface = IMG_Load("button_randomize.png");
if(image_surface == NULL)
{
printf("Cannot find image\n"); // This line is not executed
SDL_Quit();
}
SDL_Texture *image_texture = SDL_CreateTextureFromSurface(renderer, image_surface);
SDL_FreeSurface(image_surface);
// ----- Main loop
int quit = 0;
while (quit == 0)
{
SDL_Event windowEvent;
while (SDL_PollEvent(&windowEvent))
{
if (windowEvent.type == SDL_QUIT)
{
quit = 1;
break;
}
}
SDL_Rect image_rect = {50, 50, 120, 32};
SDL_RenderCopy(renderer, image_texture, NULL, &image_rect);
}
// ----- Clean up
IMG_Quit();
SDL_Quit();
return 0;
}
Output:
SDL_image could not initialize
build command:
gcc -std=c11 -Wall -o obj/main.o -c src/main.c
gcc -std=c11 -Wall -o my_program obj/main.o -lSDL2 -lGL -lGLEW -lm -lSDL2_image
I have tried changing the SDL_Init flags. I even tried to change the IMG_Init flag to IMG_INIT_JPG (and testing with a .jpg image of course), but no luck.
There are two issues in this example:
IMG_Init() returns a bitmask of all the currently initted image loaders instead of 0 on success (this is why you get the error message)
You do not call SDL_RenderPresent() after drawing on the renderer (this is why you can't see anything)
Here is an example on how to init SDL_Image from the SDL2_image documentation:
// load support for the JPG and PNG image formats
int flags = IMG_INIT_JPG | IMG_INIT_PNG;
int initted = IMG_Init(flags);
if ((initted & flags) != flags) {
printf("IMG_Init: Failed to init required jpg and png support!\n");
printf("IMG_Init: %s\n", IMG_GetError());
// handle error
}
And to make the image visible add this to the end of the loop:
SDL_RenderPresent(renderer);

Problem with SDL2 not running after compiling successfully

I'm having trouble running an SDL2 program, I compiled the program successfully using this command.
gcc -g main.c -o main.exe -IC:\\SDL2\\i686-w64-mingw32\\include -LC:\\SDL2\\i686-w64-mingw32\\lib -lmingw32 -lSDL2main -lSDL2 -Wall -Werror
Now that I'm trying to run it, I expect to get a window and to log hello in the console but nothing happens and the program exits immediately.
I'm using windows 10 and using the MinGw compiler.
This is the code.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
static const int width = 800;
static const int height = 600;
int main(int argc, char **argv)
{
SDL_Log("hello");
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Hello, SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_OPENGL);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
bool running = true;
SDL_Event event;
while(running)
{
// Process events
while(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
{
running = false;
}
}
// Clear screen
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

Basic C SDL2 program won't work with TCC while it does work with GCC (Linux)

I have the following C code (for clarity, I know it's not complete code and it should handle events and stuff like that):
#!/usr/bin/tcc -run -L/usr/lib/x86_64-linux-gnu -D_REENTRANT -DSDL_MAIN_HANDLED -I/usr/include/SDL2 -lSDL2 -lGL -lGLEW
#include <GL/glew.h>
#include <SDL.h>
int main(int argc, char **argv)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window *win = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
SDL_GLContext glContext = SDL_GL_CreateContext(win);
while (1)
{
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(win);
}
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(win);
return 0;
}
If I compile this with GCC (without the shebang line), and start the executable, it works fine, but I like the simplicity of the compile and run functionality of TCC and its speed.
However, the compiled executable gives me the following console output and then hangs forever and can't even be killed with CTRL-C:
libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
I added the SDL_MAIN_HANDLED as suggested here but that didn't change anything.
Anyone?
How about proper handling of the possible errors of the SDL calls and also initializing glew?
#include <stdlib.h>
#include <SDL.h>
#include <GL/glew.h>
void exit_sdl_error(const char *message) {
SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "%s failed with SDL error %s", message, SDL_GetError());
exit(EXIT_FAILURE);
}
int main() {
const Uint32 init_flags = SDL_INIT_VIDEO;
const int gl_major = 4, gl_minor = 0;
if (!SDL_WasInit(init_flags)) {
if (SDL_Init(init_flags) != 0)
exit_sdl_error("Unable to initialize SDL");
if (SDL_InitSubSystem(init_flags) != 0)
exit_sdl_error("Unable to initialize sub system of SDL");
}
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_major))
exit_sdl_error("Unable to set gl major version");
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_minor))
exit_sdl_error("Unable to set gl minor version");
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE))
exit_sdl_error("Unable to set gl profile");
SDL_Window *window = SDL_CreateWindow("Hello World!", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!window) {
SDL_QuitSubSystem(init_flags);
exit_sdl_error("Unable to create window");
}
SDL_GLContext glContext = SDL_GL_CreateContext(window);
if (!glContext) {
SDL_DestroyWindow(window);
SDL_QuitSubSystem(init_flags);
exit_sdl_error("Unable to create context");
}
glewInit();
while (1)
{
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(window);
}
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
SDL_QuitSubSystem(init_flags);
}

How to render text in SDL2?

I'm using an SDL_Window and SDL_Renderer.
Is it possible to use SDL_TTF with SDL_Render/SDL_Window? If so, how?
Yep, it is possible, given that you have a renderer and a window plus you don't really have any thoughts on dabbling with surfaces then you might want to mind on creating texture, here is a sample code
//this opens a font style and sets a size
TTF_Font* Sans = TTF_OpenFont("Sans.ttf", 24);
// this is the color in rgb format,
// maxing out all would give you the color white,
// and it will be your text's color
SDL_Color White = {255, 255, 255};
// as TTF_RenderText_Solid could only be used on
// SDL_Surface then you have to create the surface first
SDL_Surface* surfaceMessage =
TTF_RenderText_Solid(Sans, "put your text here", White);
// now you can convert it into a texture
SDL_Texture* Message = SDL_CreateTextureFromSurface(renderer, surfaceMessage);
SDL_Rect Message_rect; //create a rect
Message_rect.x = 0; //controls the rect's x coordinate
Message_rect.y = 0; // controls the rect's y coordinte
Message_rect.w = 100; // controls the width of the rect
Message_rect.h = 100; // controls the height of the rect
// (0,0) is on the top left of the window/screen,
// think a rect as the text's box,
// that way it would be very simple to understand
// Now since it's a texture, you have to put RenderCopy
// in your game loop area, the area where the whole code executes
// you put the renderer's name first, the Message,
// the crop size (you can ignore this if you don't want
// to dabble with cropping), and the rect which is the size
// and coordinate of your texture
SDL_RenderCopy(renderer, Message, NULL, &Message_rect);
// Don't forget to free your surface and texture
SDL_FreeSurface(surfaceMessage);
SDL_DestroyTexture(Message);
I tried to explain the code line by line, you don't see any window right there since I already assumed that you knew how to initialize a renderer which would give me an idea that you also know how to initialize a window, then all you need is the idea on how to initialize a texture.
Minor questions here, did your window open? was it colored black? if so then my thoughts were right, if not, then you can just ask me and I could change this code to implement the whole section which consists of a renderer and a window.
SDL_ttf minimal runnable example
Not super efficient, but easy to integrate. For efficiency see: How to render fonts and text with SDL2 efficiently?
Kept in a separate repo than the main SDL source, but hosted on the same official server, so should be fine: http://hg.libsdl.org/SDL_ttf/
Newlines won't work. You have to work with line heights.
Compile and run:
sudo apt-get install -y libsdl2-dev
gcc -lSDL2 -lSDL2_ttf -o ttf ttf.c
./ttf /usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf
You must pass the path of a TTF font file to the program.
ttf.c
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#define WINDOW_WIDTH 300
#define WINDOW_HEIGHT (WINDOW_WIDTH)
/*
- x, y: upper left corner.
- texture, rect: outputs.
*/
void get_text_and_rect(SDL_Renderer *renderer, int x, int y, char *text,
TTF_Font *font, SDL_Texture **texture, SDL_Rect *rect) {
int text_width;
int text_height;
SDL_Surface *surface;
SDL_Color textColor = {255, 255, 255, 0};
surface = TTF_RenderText_Solid(font, text, textColor);
*texture = SDL_CreateTextureFromSurface(renderer, surface);
text_width = surface->w;
text_height = surface->h;
SDL_FreeSurface(surface);
rect->x = x;
rect->y = y;
rect->w = text_width;
rect->h = text_height;
}
int main(int argc, char **argv) {
SDL_Event event;
SDL_Rect rect1, rect2;
SDL_Renderer *renderer;
SDL_Texture *texture1, *texture2;
SDL_Window *window;
char *font_path;
int quit;
if (argc == 1) {
font_path = "FreeSans.ttf";
} else if (argc == 2) {
font_path = argv[1];
} else {
fprintf(stderr, "error: too many arguments\n");
exit(EXIT_FAILURE);
}
/* Inint TTF. */
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(WINDOW_WIDTH, WINDOW_WIDTH, 0, &window, &renderer);
TTF_Init();
TTF_Font *font = TTF_OpenFont(font_path, 24);
if (font == NULL) {
fprintf(stderr, "error: font not found\n");
exit(EXIT_FAILURE);
}
get_text_and_rect(renderer, 0, 0, "hello", font, &texture1, &rect1);
get_text_and_rect(renderer, 0, rect1.y + rect1.h, "world", font, &texture2, &rect2);
quit = 0;
while (!quit) {
while (SDL_PollEvent(&event) == 1) {
if (event.type == SDL_QUIT) {
quit = 1;
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
/* Use TTF textures. */
SDL_RenderCopy(renderer, texture1, NULL, &rect1);
SDL_RenderCopy(renderer, texture2, NULL, &rect2);
SDL_RenderPresent(renderer);
}
/* Deinit TTF. */
SDL_DestroyTexture(texture1);
SDL_DestroyTexture(texture2);
TTF_Quit();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
GitHub upstream.
Tested in Ubuntu 16.04, SDL 2.0.4.
Yes it is. You create a surface with the text you want and then convert it to a texture that you can render.
Some sample code from one of my projects:
std::string score_text = "score: " + std::to_string(score);
SDL_Color textColor = { 255, 255, 255, 0 };
SDL_Surface* textSurface = TTF_RenderText_Solid(font, score_text.c_str(), textColor);
SDL_Texture* text = SDL_CreateTextureFromSurface(renderer, textSurface);
int text_width = textSurface->w;
int text_height = textSurface->h;
SDL_FreeSurface(textSurface);
SDL_Rect renderQuad = { 20, win_height - 30, text_width, text_height };
SDL_RenderCopy(renderer, text, NULL, &renderQuad);
SDL_DestroyTexture(text);
This assumes you've properly initialized SDL_ttf and loaded a font. In the example scoreis an int. The screen gets cleared and rendered to somewhere else (I didn't include that part).
For a full working example, check out the tutorial for SDL_ttf in SDL2 at Lazy Foo.
Edited to make it easier to follow along, to use Roboto.ttf (https://fonts.google.com/specimen/Roboto) instead of Verdana.ttf and to add not2qubit suggestion. Please keep in mind that this doesn't follow any C++ class convention at all. I just wanted to be sure that this would be easy enough to copy/paste and run.
To build this you need to add the library SDL_ttf (https://www.libsdl.org/projects/SDL_ttf/).
g++ demo.cpp -o demo -Wall -I include -lsdl2 -lsdl2_ttf
Since there are some people struggling with more complex code, I've included my own snippet here to help some beginners like myself. This will just show a red screen with a black hello world. Don't forget to add -lsdl2 and -lsdl2_ttf on your build and include the Verdana.ttf font on the same folder.
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h> //This is an sample library not included with stock SDL2. https://www.libsdl.org/projects/SDL_ttf/release-1.2.html
const char* WINDOW_TITLE = "Hello World SDL2 + TTF";
const char* FONT_NAME = "roboto.ttf";
const int FONT_SIZE = 128;
const int WINDOW_WIDTH = 1280, WINDOW_HEIGHT = 720;
SDL_Window* Window; // Window created by SDL.
SDL_Renderer* Renderer; // The renderer that shows our textures.
SDL_Event WindowEvent; // Event capturer from SDL Window.
SDL_Color TextColor = { 255, 0, 0, 255}; // Red SDL color.
TTF_Font* Font; // The font to be loaded from the ttf file.
SDL_Surface* TextSurface; // The surface necessary to create the font texture.
SDL_Texture* TextTexture; // The font texture prepared for render.
SDL_Rect TextRect; // Text rectangle area with the position for the texture text.
void CreateWindow() {
Window = SDL_CreateWindow(WINDOW_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_ALLOW_HIGHDPI);
if (!Window)
std::cout << "There was a problem creating the window.";
Renderer = SDL_CreateRenderer(Window, -1, 0);
if (!Renderer)
std::cout << "There was a problem creating the renderer.";
}
void CreateText(const char* Message) {
TTF_Init();
TTF_Font *font = TTF_OpenFont(FONT_NAME, FONT_SIZE);
if (!font)
std::cout << "Couldn't find/init open ttf font." << std::endl;
TextSurface = TTF_RenderText_Solid(font, Message, TextColor);
TextTexture = SDL_CreateTextureFromSurface(Renderer, TextSurface);
TextRect.x = WINDOW_WIDTH - TextSurface->w * 0.5; // Center horizontaly
TextRect.y = WINDOW_HEIGHT - TextSurface->h * 0.5; // Center verticaly
TextRect.w = TextSurface->w;
TextRect.h = TextSurface->h;
// After you create the texture you can release the surface memory allocation because we actually render the texture not the surface.
SDL_FreeSurface(TextSurface);
TTF_Quit();
}
bool IsPollingEvent() {
while(SDL_PollEvent(&WindowEvent)) {
switch (WindowEvent.type) {
case SDL_QUIT: return false;
}
}
return true;
}
void RenderText() {
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255); // Make window bg black.
SDL_RenderClear(Renderer); // Paint screen black.
SDL_RenderCopy(Renderer, TextTexture, NULL, &TextRect); // Add text to render queue.
SDL_RenderPresent(Renderer); // Render everything that's on the queue.
SDL_Delay(10); // Delay to prevent CPU overhead as suggested by the user `not2qubit`
}
void ClearMemory() {
SDL_DestroyTexture(TextTexture);
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
std::cout << "Clear proccess done." << std::endl;
}
int main() {
CreateWindow();
CreateText("Hello SDL_Ttf");
while (IsPollingEvent()) {
RenderText();
}
ClearMemory();
return EXIT_SUCCESS;
}
For Powershell on Windows
If you're trying to do this from Powershell on Windows, you'll soon find out that this is a real PITA. Until now...
I've just spent the hours to debug all the details to get this to work, when you want to insist using Clang++ and SDL2 to render a native Windows window with text.
There are 3 things you need to install; LLVM, SDL2, SDL2_ttf. Then you have to ensure your program will find your libraries, headers and fonts. This is basically summarized in the following program here:
//---------------------------------------------------------------------
// Name: HelloSDL2.cpp
// Author: EAML
// Date: 2021-05-16
//
// Description:
// A minimal PoC for producing a native SDL2 Windows app that can
// be ran from either Windows Explorer or from Powershell console.
// It's designed to use minimal command line, compiler options,
// and dependencies... It will display a gray window for 2 sec's.
//
// Dependencies:
// [1] LLVM Clang++ compiler package
// [2] SDL2 Libraries (DLL's) and Header files (*.h)
// [3] TTF Libraries (DLL's) and Header files (*.h)
//
// Notes:
// There is a slight variation in the bahaviour, depending on:
// (a) if you compile as a Windows GUI: the text will not show.
// (b) if you compile as a console CLI: text will show in both terminal and/or in a 2nd new window
// (c) You may need to use "main()" for console and "WinMain()" for GUI...
// (c) to install on Linux, use packages: clang, libsdl2-dev
// (d) Someone said: #define SDL_MAIN_HANDLED ...
//
// To Run:
// cp .\SDL2\lib\x64\SDL2.dll C:\Windows\. # For SDL2
// cp .\SDL2_ttf\lib\x64\*.dll C:\Windows\. # For SDL2 TTF
// cp C:\Windows\Fonts\arial.ttf . # Get a font...
//
// For a CLI version, with console output in 2nd Window:
// # clang++.exe -std=c++11 main.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:console
//
// For a GUI version, without any console output:
// # clang++.exe -std=c++11 main.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:windows
//
// References:
// [1] https://github.com/llvm/llvm-project/releases
// [2] http://www.libsdl.org/release/SDL2-devel-2.0.14-VC.zip
// [3] https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-VC.zip
// [4] https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html
// [5] http://www.sdltutorials.com/sdl-ttf
//---------------------------------------------------------------------
//#include <SDL2/SDL.h>
#include "SDL2/include/SDL.h"
#include "SDL2_ttf/include/SDL_ttf.h"
#include <stdio.h>
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define WINDOW_TITLE "Hello SDL2!"
//#define WINDOW_TEXT "Hello World!"
void drawText ( SDL_Surface* screen, char* string, int size, int x, int y, SDL_Color fgC, SDL_Color bgC) {
// Remember to call TTF_Init(), TTF_Quit(), before/after using this function.
TTF_Font* font = TTF_OpenFont("arial.ttf", size);
if(!font) {
printf("[ERROR] TTF_OpenFont() Failed with: %s\n", TTF_GetError());
exit(2);
}
TTF_SetFontStyle(font, TTF_STYLE_BOLD);
//SDL_Surface* textSurface = TTF_RenderText_Solid(font, string, fgC);
SDL_Surface* textSurface = TTF_RenderText_Shaded(font, string, fgC, bgC);
SDL_Rect textLocation = { x, y, 0, 0 };
SDL_BlitSurface(textSurface, NULL, screen, &textLocation);
SDL_FreeSurface(textSurface);
TTF_CloseFont(font);
//printf("Oh My Goodness, an error : %s\n", TTF_GetError()); return 1;
}
int main(int argc, char* args[]) {
SDL_Window* window = NULL; // The window we are rendering to
SDL_Surface* screenSurface = NULL; // The surface contained by the window
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError());
return 1;
}
window = SDL_CreateWindow(WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL) {
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError());
return 1;
}
screenSurface = SDL_GetWindowSurface(window);
SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0x80, 0x80, 0x80)); // Set a gray background canvas
SDL_UpdateWindowSurface(window);
//-----------------------------------------------------
// Draw the Text
//-----------------------------------------------------
if(TTF_Init() == -1) {
printf("[ERROR] TTF_Init() Failed with: %s\n", TTF_GetError());
exit(2);
}
SDL_Color fgC1 = { 0xff,0xff,0xff }, bgC1 = {0x00,0x00,0xa0}; // white text on blue background
SDL_Color fgC2 = { 0x00,0x00,0x00 }, bgC2 = {0xff,0x00,0xff}; // black text on magenta background
drawText( screenSurface, (char*) "Hello World! # (x=50, y=100)", 18, 50,100, fgC1, bgC1); // 18 pt # (x=100,y=150)
drawText( screenSurface, (char*) "arial.ttf # (x=200, y=150)", 16, 200,150, fgC2, bgC2); // 16 pt # (x=100,y=150)
SDL_UpdateWindowSurface(window);
TTF_Quit();
//-----------------------------------------------------
// Get some info...
//-----------------------------------------------------
SDL_version compiled;
SDL_version linked;
SDL_version ttfv;
SDL_VERSION(&compiled);
SDL_GetVersion(&linked);
SDL_TTF_VERSION(&ttfv);
printf("Compiled using SDL version : %d.%d.%d \n", compiled.major, compiled.minor, compiled.patch);
printf("and linked with SDL version : %d.%d.%d \n", linked.major, linked.minor, linked.patch);
printf("and using SDL_TTF version : %d.%d.%d \n", ttfv.major, ttfv.minor, ttfv.patch);
SDL_Delay(3000); // Wait 3 seconds
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
The result of running the code above is this:
How to get to this point?
Install LLVM for Windows:
Check the box for [x] add to Windows PATH.
If you didn't add LLVM to your Windows PATH, then at least add it temporarily and manually.
Open a Powershell and type: $env:path += ";C:\Program Files\LLVM\bin"
Install SDL2 for Windows:
Download and extract the SDL2 & SDL2_ttf runtime binaries (*.dll) and header libraries (found in [2,3])
and put them into separate SDL folder(s) in the same directory as your C++ file.
You should now have something like:
# tree --dirsfirst ./SDL2{,_ttf} -P *.h
./SDL2
├── include
│   ├── begin_code.h
│   ├── close_code.h
│   ├── SDL.h
│   ├── SDL_assert.h
...
│   ├── SDL_version.h
│   ├── SDL_video.h
│   └── SDL_vulkan.h
└── lib
./SDL2_ttf
└── include
   └── SDL_ttf.h
# tree --dirsfirst ./SDL2{,_ttf}/lib -P *.dll
./SDL2/lib
├── x64
│   └── SDL2.dll
└── x86
└── SDL2.dll
./SDL2_ttf/lib
├── x64
│   ├── libfreetype-6.dll
│   ├── SDL2_ttf.dll
│   └── zlib1.dll
└── x86
├── libfreetype-6.dll
├── SDL2_ttf.dll
└── zlib1.dll
Copy all the relevant downloaded DLL's into the C:\Windows\, unless you know how to make clang++.exe able to find them. (I wasn't able...)
cd C:\path\to\main.cpp
cp .\SDL2\lib\x64\SDL2.dll C:\Windows\. # For SDL2
cp .\SDL2_ttf\lib\x64\*.dll C:\Windows\. # For SDL2 TTF
cp C:\Windows\Fonts\arial.ttf . # Get a font...
Download the above SDL2 "Hello World" Windows program.
Use the Minimal PoC code from here.
Compile the program with:
clang++.exe -std=c++11 main2.cpp -o main.exe -L .\SDL2\lib\x64\ -L .\SDL2_ttf\lib\x64\ -I .\SDL2_ttf\include\ -I .\SDL2\include\ -lShell32 -lSDL2main -lSDL2 -lSDL2_ttf -Wno-narrowing -Xlinker /subsystem:windows
The order of how the libraries are placed, seem to have an importance. Make sure it's like above.
Also, notice the two different -Xlinker options:
/subsystem:windows # This give you only one window but no console output
/subsystem:console # This give you console output, but in a 2nd window when in GUI
To see other linker optins, use:
link.exe /link
link.exe /lib
# The most relevant are:
/DLL
/ENTRY:symbol
/LIBPATH:dir
/MACHINE:{ARM|ARM64|EBC|X64|X86}
/SUBSYSTEM:{CONSOLE | NATIVE | POSIX | WINDOWS | WINDOWSCE |...}
/VERBOSE
You are now good to go!
Download References
LLVM Clang++ compiler package
SDL2 Libraries (DLL's) and Header files (*.h)
SDL2 TTF Libraries (DLL's) and Header files (*.h)
Useful SDL wiki
If you want it to do with class:
// Include somewhere
#include <SDL2/SDL_ttf.h>
// Do not forget to init TTF once somewhere main.cpp
if (TTF_Init()==-1) {
printf("Failed to TTF: %s", SDL_GetError());
return 1;
}
// Have a font instance ready
TTF_Font* font = TTF_OpenFont("assets/fonts/ugly.ttf", 72);
if (font==NULL){
printf("Failed to load font: %s", SDL_GetError());
}
// Have a Text instance ready
// (Class file is below this code)
// Also you need to provide SDL_Renderer* renderer to init
Text* fps_tracker = new Text(renderer, font);
// Somewhere in while true
fps_tracker.setText("FPS: 232");
// Render it
fps_tracker.render();
Here is the class:
using namespace std;
#include <string>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
class Text {
SDL_Texture* texture = NULL;
SDL_Rect position;
TTF_Font* font;
SDL_Renderer* renderer;
SDL_Color color;
string text;
public:
Text(SDL_Renderer* renderer, TTF_Font* font, string text="", int x=0, int y=0, SDL_Color color={255, 255, 255}) {
position.x = x;
position.y = y;
this->color = color;
this->font = font;
this->renderer = renderer;
}
void setText(string text) {
if (this->text==text){
return;
}
this->text = text;
SDL_DestroyTexture(texture);
SDL_Surface* surface = TTF_RenderText_Solid(font, text.c_str(), color);
if (surface==NULL) {
printf("Failed to render text: %s", SDL_GetError());
}
position.w = surface->w;
position.h = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
}
void render() {
SDL_RenderCopy(renderer, texture, NULL, &position);
}
~Text() {
SDL_DestroyTexture(texture);
}
};
If you don't want to or can't use the SDL2 TTF library, you can easily implement it yourself with just the freetype library.
Include freetype
#include <ft2build.h>
#include FT_FREETYPE_H
Create texture class
class texture
{
public:
texture() : t{nullptr} {}
texture(const texture &) = delete;
texture &operator=(const texture &) = delete;
texture(texture &&o) : t{o.t}
{
o.t = nullptr;
}
inline texture &operator=(texture &&o)
{
release();
t = o.t;
o.t = nullptr;
return *this;
}
inline texture(SDL_Renderer *renderer, void *image, int width, int height, int depth, int pitch, uint32_t rmask, uint32_t gmask, uint32_t bmask, uint32_t amask) : t{nullptr}
{
attach_image(renderer, image, width, height, depth, pitch, rmask, gmask, bmask, amask);
}
inline void attach_image(SDL_Renderer *renderer, void *image, int width, int height, int depth, int pitch, uint32_t rmask, uint32_t gmask, uint32_t bmask, uint32_t amask)
{
release();
SDL_Surface *s = SDL_CreateRGBSurfaceFrom(image, width, height, depth, pitch, rmask, gmask, bmask, amask);
t = SDL_CreateTextureFromSurface(renderer, s);
SDL_FreeSurface(s);
}
inline void draw(SDL_Renderer *renderer, const SDL_Rect *src, const SDL_Rect *dest) const
{
if (t)
SDL_RenderCopyEx(renderer, t, src, dest, 0, nullptr, SDL_FLIP_NONE);
}
int width() const
{
if(!t) return 0;
int w;
SDL_QueryTexture(t, nullptr, nullptr, &w, nullptr);
return w;
}
int height() const {
if(!t) return 0;
int h;
SDL_QueryTexture(t, nullptr, nullptr, nullptr, &h);
return h;
}
~texture()
{
release();
}
private:
SDL_Texture *t;
inline void release()
{
if (t)
SDL_DestroyTexture(t);
t = nullptr;
}
};
Create glyph class
struct character : texture
{
using texture::texture;
unsigned int advance;
int bearing_x;
int bearing_y;
};
Determine endianness and set up masks
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define rmask 0x000000ff
#define gmask 0x0000ff00
#define bmask 0x00ff0000
#define amask 0xff000000
#else
#define rmask 0xff000000
#define gmask 0x00ff0000
#define bmask 0x0000ff00
#define amask 0x000000ff
#endif
Create converter from free type's 8bit pixel depth to 32bit for SDL2
void convert_8_to_32_depth(std::vector<uint32_t> &res, unsigned char *image, int width, int height)
{
res.clear();
res.reserve(width * height);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
if (image[y * width + x])
res.push_back(amask);
else
res.push_back(0);
}
}
}
Create font storage texture class
struct lib
{
lib() = default;
lib(SDL_Renderer *renderer, int height)
{
init(renderer, height);
}
void init(SDL_Renderer *renderer, int height)
{
FT_Library ft;
if (FT_Init_FreeType(&ft))
{
std::cout << "Can't init freetype lib\n";
}
FT_Face face;
//use if you have font data in array
// if (FT_New_Memory_Face(ft, font_array, std::size(font_array), 0, &face))
// {
// std::cout << "Failed to load font\n";
// }
if (FT_New_Face(ft, "location/to/my/font.ttf", 0, &face))
{
std::cout << "Failed to load font\n";
}
//set size of future glyphs
FT_Set_Pixel_Sizes(face, 0, height);
std::vector<uint32_t> image;
for (unsigned int c = 0; c < 256; ++c)
{
//load freetype glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER))
{
std::cout << "failed to load glyph\n";
}
if (face->glyph->bitmap.width)
{
///get image data that works for sdl2
convert_8_to_32_depth(image, face->glyph->bitmap.buffer, face->glyph->bitmap.width, face->glyph->bitmap.rows);
chars[c].attach_image(renderer, image.data(), face->glyph->bitmap.width, face->glyph->bitmap.rows,
32, face->glyph->bitmap.width * sizeof(decltype(image)::value_type),
rmask, gmask, bmask, amask);
}
chars[c].bearing_x = face->glyph->bitmap_left;
chars[c].bearing_y = face->glyph->bitmap_top;
chars[c].advance = face->glyph->advance.x;
}
FT_Done_Face(face);
FT_Done_FreeType(ft);
}
character chars[256];
};
Print text function
void print_text(SDL_Renderer *renderer, int x, int y, int height, std::string_view text)
{
static constexpr int default_height = 50;
//store map of each renderer used to avoid creating more libs than neccesary
static std::map<SDL_Renderer *, lib> l;
const lib& ts = l.try_emplace(renderer, renderer, default_height).first->second;
float scale = height / default_height;
SDL_Rect dest;
for (auto c : text)
{
dest.x = x + ts.chars[c].bearing_x * scale;
dest.y = y - ts.chars[c].bearing_y * scale;
dest.w = ts.chars[c].width() * scale;
dest.h = ts.chars[c].height() * scale;
ts.chars[c].draw(renderer, nullptr, &dest);
x += (ts.chars[c].advance >> 6) * scale;
}
}

Resources