I have a problem with this program that reads audio data from an input device and displays a line representing the volume level. It starts ok then a few seconds later it starts lagging. It worked without slow downs until I tried to add some code to add image display functionality, which didn't work so I removed it, but now it the program doesn't work properly. I've removed most of the program functionality which I'll add back if I fix it. The CPU and GPU usage remains low so no problems there. If I switch to Software Mode it seems to work. I'm on Windows using MinGW-w64.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <math.h>
int SCREEN_WIDTH = 1024; //default
int SCREEN_HEIGHT = 768; //default
int FULLSCREEN = 0;
SDL_Renderer *rend;
//#define SOFTWARE_RENDER
//#define VSYNC_ON
#define PROGRAM_NAME "My Game"
#ifdef SOFTWARE_RENDER
#define RENDERER SDL_RENDERER_SOFTWARE
#else
#define RENDERER SDL_RENDERER_ACCELERATED
#endif
#if !defined(SOFTWARE_RENDER) && defined(VSYNC_ON)
#define VSYNC SDL_RENDERER_PRESENTVSYNC
#else
#define VSYNC 0
#endif
////https://wiki.libsdl.org/SDL_AudioSpec#callback
void audioInCallback(void *userdata, Uint8 *stream,int len)
{
float *floatStream = (float*)stream;
if (stream == NULL)
{
puts("Stream is NULL.");
return;
}
SDL_SetRenderDrawColor(rend, 0, 0, 0, 255);
SDL_RenderClear(rend);
SDL_SetRenderDrawColor(rend, 255, 255, 255, 255);
float avg = 0;
for (int i=0;i<len;i++)
avg += fabs(floatStream[i]);
avg /= len;
SDL_RenderDrawLine(rend, 0,
SCREEN_HEIGHT/2 + round(SCREEN_HEIGHT/2.0 * avg),
SCREEN_WIDTH,
SCREEN_HEIGHT/2 + round(SCREEN_HEIGHT/2.0 * avg)
);
return;
}
int main(int argv, char *argc[])
{
int bufferSize = 8;
SDL_Window* window = NULL;
rend = NULL;
SDL_Event event;
bool loopIsActive = true;
SDL_AudioSpec want, have;
SDL_AudioDeviceID dev;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0)
{
printf("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
if( (window = SDL_CreateWindow( PROGRAM_NAME, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN )) == NULL )
{
printf("Window could not be created! %s\n", SDL_GetError());
SDL_Quit();
return 0;
}
if ( (rend = SDL_CreateRenderer(window, -1, RENDERER | VSYNC)) == NULL )
{
printf("Error creating renderer. %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
int count = SDL_GetNumAudioDevices(1);
for (int i=0;i<count;i++)
{
printf("%d. %s\n", i, SDL_GetAudioDeviceName(i, 1));
}
SDL_memset(&want, 0, sizeof(want)); /* or SDL_zero(want) */
want.freq = 44100;
want.format = AUDIO_S16;
want.channels = 1;
want.samples = pow(2,bufferSize);
want.callback = audioInCallback;
dev = SDL_OpenAudioDevice(NULL, 1, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (dev == 0)
printf("Failed to open audio: %s", SDL_GetError());
else
{
printf("have.samples = %u\n", have.samples);
printf("Opened device %s\nAudio format: ", SDL_GetAudioDeviceName(0, 1));
if (SDL_AUDIO_ISFLOAT(have.format))
printf("%d-bit %s\n", SDL_AUDIO_BITSIZE(have.format), SDL_AUDIO_ISFLOAT(have.format) ? "float" :
SDL_AUDIO_ISSIGNED(have.format) ? "signed" : "unsigned");
SDL_PauseAudioDevice(dev, 0); /* start audio playing. */
}
while (loopIsActive == true)
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
loopIsActive = false;
break;
case SDL_KEYDOWN:
if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
{
loopIsActive = false;
}
break;
}
}
SDL_RenderPresent(rend);
SDL_Delay(16);
}
if (dev != 0) SDL_CloseAudioDevice(dev);
SDL_DestroyRenderer(rend);
SDL_DestroyWindow(window);
SDL_Quit();
puts("Hello world!");
return 0;
}
Don't try to do SDL_Renderer operations in a different thread like the one the audio callback is typically called from. As #keltar pointed out this is prohibited by SDL:
These functions must be called from the main thread.
Instead, bundle up the audio samples via your favorite method for rendering on the main thread (sorry for the C++, not much of a C guy re: threading primitives):
#include <SDL.h>
#include <vector>
#include <atomic>
#include <iostream>
// triple-buffering logic stol^H^H^H^Hborrowed from:
// https://stackoverflow.com/questions/49352853
std::vector< float >* g_ProducerBuffer;
std::vector< float >* g_ConsumerBuffer;
std::atomic< std::vector< float >* > g_CurrentBuffer;
void audioInCallback( void* userdata, Uint8* stream, int len )
{
const float* floatStream = reinterpret_cast< float* >( stream );
for( auto& sample : *g_ProducerBuffer )
{
sample = *floatStream;
floatStream++;
}
// post new buffer
g_ProducerBuffer = g_CurrentBuffer.exchange( g_ProducerBuffer );
}
int main( int argc, char** argv )
{
SDL_Init( SDL_INIT_EVERYTHING );
SDL_SetHint( SDL_HINT_RENDER_VSYNC, "1" );
//SDL_SetHint( SDL_HINT_RENDER_DRIVER, "software" );
Uint32 flags = SDL_WINDOW_SHOWN;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_CreateWindowAndRenderer( 1024, 768, flags, &window, &renderer );
// dump device names
int count = SDL_GetNumAudioDevices( 1 );
for( int i = 0; i < count; i++ )
{
std::cout << i << ": " << SDL_GetAudioDeviceName( i, 1 ) << '\n';
}
SDL_AudioSpec spec = { 0 };
spec.freq = 44100;
spec.format = AUDIO_F32LSB;
spec.channels = 1;
spec.samples = 1024;
spec.callback = audioInCallback;
const unsigned int deviceIndex = 0;
const std::string deviceName = SDL_GetAudioDeviceName( deviceIndex, 1 );
SDL_AudioDeviceID dev = SDL_OpenAudioDevice( deviceName.c_str(), 1, &spec, nullptr, 0 );
// set up audio buffers
std::vector< float > buffers[ 3 ];
buffers[ 0 ].resize( spec.samples );
buffers[ 1 ].resize( spec.samples );
buffers[ 2 ].resize( spec.samples );
g_ProducerBuffer = &buffers[ 0 ];
g_ConsumerBuffer = &buffers[ 1 ];
g_CurrentBuffer = &buffers[ 2 ];
// start audio capture
SDL_PauseAudioDevice( dev, 0 );
bool running = true;
while( running )
{
SDL_Event ev;
while( running && SDL_PollEvent( &ev ) )
{
if( SDL_QUIT == ev.type ||
SDL_KEYDOWN == ev.type && SDL_SCANCODE_ESCAPE == ev.key.keysym.scancode )
{
running = false;
}
}
SDL_SetRenderDrawColor( renderer, 0, 0, 0, 255 );
SDL_RenderClear( renderer );
// grab latest audio buffer
// (this *seems* to work on my system but I'm not 100% sure it's correct)
while( !g_CurrentBuffer.compare_exchange_weak( g_ConsumerBuffer, g_ConsumerBuffer ) );
// draw audio sample waveform
std::vector< SDL_Point > points( g_ConsumerBuffer->size() );
for( size_t i = 0; i < g_ConsumerBuffer->size(); ++i )
{
const int x = static_cast< int >( i );
const int y = static_cast< int >( (*g_ConsumerBuffer)[ i ] * 200 + 768 / 2 );
points[ i ] = SDL_Point{ x, y };
}
SDL_SetRenderDrawColor( renderer, 255, 255, 255, 255 );
SDL_RenderDrawLines( renderer, points.data(), points.size() );
SDL_RenderPresent( renderer );
}
SDL_CloseAudioDevice( dev );
SDL_DestroyRenderer( renderer );
SDL_DestroyWindow( window );
SDL_Quit();
return 0;
}
Related
Can someone help me to increase the length of the snake and make it follow the head ?
I tried to develop a simple snake game without videos or help and did almost great but cannot figure out how to increase the size of the snake and move it properly.
I'd be very thankful
#include <SDL2/SDL.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <time.h>
#define LOG(x) printf("%s\n", x)
#define LOG_ERROR(x) fprintf(stderr, "Error: %s\n", x)
#define LOG_SDL_ERROR(x) fprintf(stderr, "%s: %s\n", x, SDL_GetError())
#define global_variable static
#define internal_function static
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define HEAD_SIZE 30
#define APPLE_SIZE 25
#define MAX_LENGHT 10
typedef const char* string;
typedef float realNum;
typedef struct Snake
{
SDL_Rect body[MAX_LENGHT];
int xSpeed;
int ySpeed;
int site;
realNum xPos;
realNum yPos;
} Snake;
typedef struct Apple
{
int xPos;
int yPos;
int size;
} Apple;
void checkColissions(Apple *apple, Snake *snake );
bool initGame(void);
void update(realNum);
void handleEvent(SDL_Event);
void renderGame(void);
void gameOver(void);
void shutdownGame(void);
Snake makeSnake(void);
void renderSnake(Snake *snake);
void updateSnake(Snake *, realNum);
Apple makeApple();
void renderApple(Apple *);
global_variable bool Running;
global_variable SDL_Window *g_pWindow;
global_variable SDL_Renderer *g_pRenderer;
Snake *g_pSnake = NULL;
Apple *g_pApple = NULL;
int
main(int argc, char const *argv[])
{
srand((unsigned int)time(NULL));
atexit(shutdownGame);
if (!initGame())
{
LOG_ERROR("Failed Initialization");
exit(1);
}
else
{
Running = true;
int fps = 330;
int desiredDelta = 1000/fps;
SDL_Event event;
while (Running)
{
renderGame();
update(desiredDelta);
handleEvent(event);
SDL_Delay(rand() % 30);
}
}
return (EXIT_SUCCESS);
}
bool
initGame()
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
LOG_SDL_ERROR("Failed initialization: ");
return (false);
}
else
{
g_pWindow =
SDL_CreateWindow("Snake", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
g_pRenderer =
SDL_CreateRenderer(g_pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
g_pSnake = (Snake*)malloc(sizeof(Snake));
g_pApple = (Apple*)malloc(sizeof(Apple));
*g_pSnake = makeSnake();
*g_pApple = makeApple();
if ((g_pApple || g_pSnake || g_pRenderer || g_pWindow) == NULL)
{
return (false);
}
g_pWindow == NULL ? LOG_ERROR("Window failed") : LOG("Succes Window");
g_pRenderer == NULL ? LOG_ERROR("Renderer failed") : LOG("Succes Renderer");
g_pApple == NULL ? LOG_ERROR("Apple failed") : LOG("Succes Apple");
g_pSnake == NULL ? LOG_ERROR("Snake failed") : LOG("Succes Snake");
LOG("Game initialized");
}
return true;
}
void
renderGame()
{
SDL_SetRenderDrawColor(g_pRenderer, 0, 80, 0, 255);
SDL_RenderClear(g_pRenderer);
renderSnake(g_pSnake);
renderApple(g_pApple);
SDL_RenderPresent(g_pRenderer);
}
void update(realNum elapsed)
{
updateSnake(g_pSnake, elapsed);
checkColissions(g_pApple, g_pSnake);
}
void
handleEvent(SDL_Event event)
{
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
{
Running = false;
} break;
default:
break;
}
}
void
shutdownGame()
{
if (g_pWindow)
{
SDL_DestroyWindow(g_pWindow);
}
if (g_pRenderer)
{
SDL_DestroyRenderer(g_pRenderer);
}
if (g_pApple)
{
free(g_pApple);
}
if (g_pSnake)
{
free(g_pSnake);
}
SDL_Quit();
LOG("Game shutdowned");
}
Snake makeSnake(void)
{
const int speed = 0;
Snake snake = {
.xSpeed = speed,
.ySpeed = speed,
.xPos = SCREEN_WIDTH / 2,
.yPos = SCREEN_HEIGHT / 2,
.site = HEAD_SIZE
};
SDL_Rect SnakeRect = {
.h = snake.site,
.w = snake.site,
.x = snake.xPos,
.y = snake.yPos
};
snake.body[0] = SnakeRect;
return (snake);
}
void renderSnake(Snake *snake)
{
for (int i = 0; i < MAX_LENGHT; i++)
{
SDL_SetRenderDrawColor(g_pRenderer, 0, 0, 100, 255);
SDL_RenderFillRect(g_pRenderer, &snake->body[i]);
}
}
void updateSnake(Snake *snake, realNum elapsed)
{
const Uint8 *keyboardState = SDL_GetKeyboardState(NULL);
for (int i = 0; i < MAX_LENGHT; i++)
{
/* code */
snake->body[i].y += snake->ySpeed * elapsed;
snake->body[i].x += snake->xSpeed * elapsed;
}
/* code */
/* code */
/* code */
if (keyboardState[SDL_SCANCODE_W])
{
snake->ySpeed = -1;
snake->xSpeed = 0;
}
if (keyboardState[SDL_SCANCODE_D])
{
snake->ySpeed = 0;
snake->xSpeed = 1;
}
if (keyboardState[SDL_SCANCODE_A])
{
snake->ySpeed = 0;
snake->xSpeed = -1;
}
if (keyboardState[SDL_SCANCODE_S])
{
snake->ySpeed = 1;
snake->xSpeed = 0;
}
if (snake->body->x < 0 - HEAD_SIZE)
{
snake->body->x = SCREEN_WIDTH + HEAD_SIZE;
}
if (snake->body->x > SCREEN_WIDTH + HEAD_SIZE)
{
snake->body->x = 0 - HEAD_SIZE;
}
if (snake->body->y < 0 - HEAD_SIZE)
{
snake->body->y = SCREEN_HEIGHT + HEAD_SIZE;
}
if (snake->body->y > SCREEN_HEIGHT + HEAD_SIZE)
{
snake->body->y = 0 - HEAD_SIZE;
}
}
void
checkColissions(Apple *apple, Snake *snake )
{
SDL_Rect appleRect = {
.h = APPLE_SIZE,
.w = APPLE_SIZE,
.x = apple->xPos,
.y = apple->yPos
};
if (SDL_HasIntersection(&appleRect, snake->body))
{
*g_pApple = makeApple();
};
}
Can someone help me to increase the length of the snake and make it follow the head ?
Snake is defined by an array of coordinates, not just one (x,y). You need some kind of circular buffer, that can change capacity. Or you can just use an array, and then change every element of the array with every snake's move. And then you would realize that circular buffer is a really neat idea.
Once you have your buffer, be it an array or circular buffer, the question becomes how to move and enlarge the snake in it. It is simple. To move the snake you add one coordinate to the head, and remove one from the tail of the buffer. When the snake should become bigger you just don't remove the last element in the buffer. Add to the head, but don't remove the tail. This is how circular buffer would be handled. In case of a plain array moving means overwriting everything, and enlarging means adding to the beginning, and moving everything else by one place.
I am working on an object of SDL Tetris. When I ran my code, it first worked fine. But it automatically stopped displaying my text a few minutes later. I guessed there must be some problem on the memory management and I proved that I was right via Task Manager.
I look over my source code but found nothing wrong. (at least I didn't find it) Every heap object I delcared was freed and textures were destroyed. So, what can be the problem?
Here's my source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
SDL_Window * window;
SDL_Renderer * renderer;
SDL_Surface * text;
TTF_Font* font;
bool running = true;
#define ScreenWidth 60
#define ScreenHeight 30
#define CharWidth 14
#define CharHeight 24
char screen[ScreenHeight][ScreenWidth + 1];
SDL_Color color = {192, 192, 192};
SDL_Rect rect;
int score = 0;
char shapes[7][17];
typedef struct Block {
int type;
char shape[17];
int x, y;
int width, height;
} Block;
Block * newBlock(int type) {
Block * block = malloc(sizeof(Block));
return block;
}
void CheckEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
} else if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_w:
;
break;
case SDLK_a:
;
break;
case SDLK_d:
;
break;
case SDLK_s:
;
break;
case SDLK_SPACE:
;
break;
default:
break;
}
}
}
free(&event);
}
void UpdateBoard() {
for (int i = 0; i < ScreenHeight; ++i) {
for (int j = 0; j < ScreenWidth; ++j) {
screen[i][j] = ' ';
}
}
for (int i = 0; i != 20; ++i) {
screen[i + 2][1] = '#';
screen[i + 2][1 + 10 + 1] = '#';
}
for (int i = 0; i != 10 + 2; ++i) {
screen[22][i + 1] = '#';
}
sprintf(&(screen[0][1]), "Score: %d", score);
for (int i = 0; i != ScreenHeight; i++) {
for (int j = 0; j != ScreenWidth; j++) {
if (screen[i][j] == '\0') {
screen[i][j] = ' ';
}
}
}
}
void UpdateScreen() {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
for (int i = 0; i < ScreenHeight; ++i) {
text = TTF_RenderText_Solid(font, screen[i], color);
SDL_Texture * texture;
texture = SDL_CreateTextureFromSurface(renderer, text);
rect.x = 0;
rect.y = i * CharHeight;
rect.w = strlen(screen[i]) * CharWidth;
rect.h = CharHeight;
SDL_RenderCopy(renderer, texture, NULL, &rect);
free(texture);
SDL_DestroyTexture(texture);
}
SDL_RenderPresent(renderer);
}
void Init() {
SDL_Init(SDL_INIT_EVERYTHING);
TTF_Init();
window = SDL_CreateWindow("Tetris", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
ScreenWidth * CharWidth, ScreenHeight * CharHeight, 0);
renderer = SDL_CreateRenderer(window, -1, 0);
font = TTF_OpenFont("s8514oem.fon", 0);
sprintf(shapes[0], "OOOO............");
sprintf(shapes[1], "OO...OO.........");
sprintf(shapes[2], ".OO.OO..........");
sprintf(shapes[3], "O...OOO.........");
sprintf(shapes[4], "..O.OOO.........");
sprintf(shapes[5], "OO..OO..........");
sprintf(shapes[6], "OOO..O..........");
}
int main(int argc, char ** argv) {
Init();
while (running) {
CheckEvents();
UpdateBoard();
UpdateScreen();
SDL_Delay(1000/16);
}
TTF_Quit();
SDL_Quit();
return 0;
}
My Makefile:
tetris:
gcc -std=c99 -Wall -Isrc/include -Lsrc/lib -o Tetris main.c -lSDL2main -lSDL2 -lSDL2_ttf
Several things:
Don't try to free() stack-allocated variables like event:
//free(&event);
text needs to be freed with SDL_FreeSurface() before replacing its pointer value with a new one from TTF_RenderText_Solid():
if(text) {
SDL_FreeSurface(text);
}
text = TTF_RenderText_Solid(font, screen[i], color);
Don't use free() on the textures returned by SDL_CreateTextureFromSurface(), only use SDL_DestroyTexture():
texture = SDL_CreateTextureFromSurface(renderer, text);
...
//free(texture);
SDL_DestroyTexture(texture);
I'm working through Lazy Foo's SDL2 tutorials and stuck trying to render multiple viewports. The idea is to load a PNG as a texture, create SDL_Rect structs for 3 different viewports (top left, top right, bottom), then copy the texture onto each viewport, and finally render all 3 viewports. My code is only rendering the first viewport.
I've tried changing the order - in every case, whichever viewport is defined first is the only one that renders.
I also tried changing the viewport dimensions, in case overlap was the issue - same problem.
The only question I found about multiple viewports didn't point me in the right direction. But it did start me thinking - my code is written in C, the tutorial in C++. Although I think I'm translating everything correctly (the other lessons work fine), maybe I'm missing something obvious here?
I'm compiling with CFLAGS = -Wall -Wextra -pedantic -std=c99 - no warnings or errors.
Edit: I tried rendering filled rectangles instead of a loaded PNG, but the issue is the same - only the first one renders.
Here's my code:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
int init_renderer();
int load_media();
SDL_Texture *load_texture(char *);
void close_renderer();
SDL_Window *g_window = NULL;
SDL_Renderer *g_renderer = NULL;
SDL_Texture *g_texture = NULL;
int main()
{
if (init_renderer() != 1) {
return -1;
}
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
printf("Warning: Linear texture filtering not enabled!\n");
}
if (load_media() != 1) {
return -1;
}
int quit = 0;
SDL_Event e;
while (quit != 1) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT || e.key.keysym.sym == SDLK_q) {
quit = 1;
}
}
SDL_SetRenderDrawColor(g_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(g_renderer);
SDL_Rect top_left_vp;
top_left_vp.x = 0;
top_left_vp.y = 0;
top_left_vp.w = SCREEN_WIDTH / 2;
top_left_vp.h = SCREEN_HEIGHT / 2;
SDL_RenderSetViewport(g_renderer, &top_left_vp);
SDL_RenderCopy(g_renderer, g_texture, NULL, NULL);
SDL_Rect top_right_vp;
top_right_vp.x = SCREEN_WIDTH / 2;
top_right_vp.y = 0;
top_right_vp.w = SCREEN_WIDTH / 2;
top_right_vp.h = SCREEN_HEIGHT / 2;
SDL_RenderSetViewport(g_renderer, &top_right_vp);
SDL_RenderCopy(g_renderer, g_texture, NULL, NULL);
SDL_Rect bottom_vp;
bottom_vp.x = 0;
bottom_vp.y = SCREEN_HEIGHT / 2;
bottom_vp.w = SCREEN_WIDTH;
bottom_vp.h = SCREEN_HEIGHT / 2;
SDL_RenderSetViewport(g_renderer, &bottom_vp);
SDL_RenderCopy(g_renderer, g_texture, NULL, NULL);
SDL_RenderPresent(g_renderer);
}
close_renderer();
return 0;
}
int init_renderer()
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("Failed to initialize SDL. Error: %s\n", SDL_GetError());
return 0;
}
g_window = SDL_CreateWindow("SDL Tuts",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN);
if (g_window == NULL) {
printf("Failed to create window. Error: %s\n", SDL_GetError());
return 0;
}
g_renderer = SDL_CreateRenderer(g_window, -1, SDL_RENDERER_ACCELERATED);
if (g_renderer == NULL) {
printf("Failed to create renderer. Error: %s\n", SDL_GetError());
return 0;
}
SDL_SetRenderDrawColor(g_renderer, 0x29, 0xAB, 0x87, 0xFF);
int img_flags = IMG_INIT_PNG;
if (!(IMG_Init(img_flags) & img_flags)) {
printf("Failed to initialize SDL Image. SDL_Image Error: %s\n", IMG_GetError());
return 0;
}
return 1;
}
int load_media()
{
g_texture = load_texture("assets/texture.png");
if (g_texture == NULL) {
return 0;
}
return 1;
}
SDL_Texture *load_texture(char *path)
{
SDL_Texture *new_texture = NULL;
SDL_Surface *loaded_surface = IMG_Load(path);
if (loaded_surface == NULL) {
printf("Failed to load image. SDL_Image Error: %s\n", IMG_GetError());
} else {
new_texture = SDL_CreateTextureFromSurface(g_renderer, loaded_surface);
if (new_texture == NULL) {
printf("Failed to create texture from %s. Error: %s\n", path, SDL_GetError());
}
SDL_FreeSurface(loaded_surface);
}
return new_texture;
}
void close_renderer()
{
SDL_DestroyTexture(g_texture);
g_texture = NULL;
SDL_DestroyRenderer(g_renderer);
SDL_DestroyWindow(g_window);
g_renderer = NULL;
g_window = NULL;
IMG_Quit();
SDL_Quit();
}
To check for more errors, you should see what the calls to SDL_RenderSetViewPort are returning. According to the docs, it returns an int, which is 0 on success or a negative int if there's an error. It's entirely possible that something deeper in SDL is having a fit.
Additional pro tips: you can define the SDL_Rect structs outside of your while loop to save a miniscule margin of processing power. And to spare yourself some typing, you can initialize a SDL_Rect using a list constructor instead of manually punching each property. E.G.:
SDL_Rect top_left_vp;
top_left_vp.x = 0;
top_left_vp.y = 0;
top_left_vp.w = SCREEN_WIDTH / 2;
top_left_vp.h = SCREEN_HEIGHT / 2;
is more simply
SDL_Rect top_left_vp = {
0,
0,
SCREEN_WIDTH / 2,
SCREEN_HEIGHT / 2
};
My program segfaults as soon as I press a key, but I am unable to come up with a minimal code. If I comment out pixels[y][pdpix] = col[sampleX][sampleY]; (line 110), key presses work as normal. Otherwise, it segfaults.
Full code and "bmp" files: https://drive.google.com/file/d/1663hiKbvodcNu0xG8FpbYACAUoGScfAn/view?usp=sharing
Code in main.c:
#include <SDL2/SDL.h>
#include <stdbool.h>
#include "read.h"
#define RESX 960
#define RESY 540
#define HRESX 480
#define DRAWDISTANCE 360
#define ROTSPEED 0.005
#define BGCOL 0
#define ARESY 539
void main() {
readCW();
bool running = true;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
running = false;
}
window = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, RESX, RESY, SDL_WINDOW_SHOWN );
if( window==NULL) {
printf( "SDL_Error: %s\n", SDL_GetError() );
running = false;
}
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
if(renderer==NULL) {
printf("Renderer error: %s\n", SDL_GetError() );
running = false;
}
SDL_Texture * texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, RESX,RESY);
readBW();
float playerX = 512;
float playerY = 512;
float dirX = 0.0;
float dirY = 1.0;
void rotatez(float rotspeed) {
float oldDirX = dirX;
dirX = dirX * cos(rotspeed) - dirY * sin(rotspeed);
dirY = oldDirX * sin(rotspeed) + dirY * cos(rotspeed);
}
const Uint8* keystate = SDL_GetKeyboardState( NULL );
while( running ) {
clock_t t1, t2;
t1 = SDL_GetTicks();
Uint32 pixels[RESY][RESX] = {[0 ... RESY-1][0 ...RESX-1] = 0x5555FF};
SDL_Event e;
running = !(SDL_PollEvent( &e ) && e.type == SDL_QUIT);
if(keystate[SDL_SCANCODE_ESCAPE]) running = false;
if(keystate[SDL_SCANCODE_LEFT]) rotatez(-ROTSPEED);
if(keystate[SDL_SCANCODE_RIGHT]) rotatez(ROTSPEED);
if(keystate[SDL_SCANCODE_W]) {
playerY += dirY;
playerX += dirX;
}
if(keystate[SDL_SCANCODE_S]) {
playerY -= dirY;
playerX -= dirX;
}
if(keystate[SDL_SCANCODE_A]) {
playerY -= dirX;
playerX += dirY;
}
if(keystate[SDL_SCANCODE_D]) {
playerY += dirX;
playerX -= dirY;
}
//rotatez(-ROTSPEED);
playerY -= dirY;//for testing
playerX -= dirX;//for testing
int heightBuffer[RESX] = {[0 ... 959] = RESY};
int playerHeight = 80;//disp[(int)playerX][(int)playerY] + 50;
int dpix = 0;
for( int dDist = 1; dDist < DRAWDISTANCE; dDist+=1) {
for( int pix = -dDist; pix < dDist; pix++) {
int pdpix = dpix;
dpix = (pix/(float)dDist+1) *HRESX;
int sampleX = (int)playerX + dDist*dirX + pix*-dirY;
int sampleY = (int)playerY + dDist*dirY + pix*dirX;
int height = disp[sampleX][sampleY];
int heightOnScreen = ( ((float)playerHeight - height) / dDist * 240 + 240); //alg for persp proj
if(heightOnScreen > 0 && heightOnScreen < RESY) {
for( pdpix; pdpix < dpix; pdpix++) {
if(heightOnScreen < heightBuffer[pdpix]) {
for(int y = heightBuffer[pdpix]; y > heightOnScreen; y-=1) {
pixels[y][pdpix] = col[sampleX][sampleY]; //if I comment out this line, segfault goes away.
}
heightBuffer[pdpix] = heightOnScreen;
}
}
}
}
}
SDL_UpdateTexture(texture, NULL, pixels, RESX * sizeof(Uint32));
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent( renderer );
t2 = SDL_GetTicks();
printf("%ld fps\n", 1000/(t2-t1));
}
SDL_DestroyWindow( window );
SDL_DestroyRenderer( renderer );
SDL_DestroyTexture( texture );
SDL_Quit();
}
I am writing an application using Cairo in C that does the following:
Load background PNG (wheel)
rotate wheel 90 degrees
draw on wheel a set of numbers from other PNG files
do this until all 6 parts of the wheel have the numbers drawn on the wheel
save the PNG to a file ( results.png )
The problem I'm having is if I don't rotate the wheel, the numbers are drawn properly as seen in this image, except the wheel isn't rotated as needed.
However, if I attempt to rotate AND draw the numbers on the wheel I get a rotated wheel with no numbers as seen in this image.
I've tried various permutations of the code, but I can't seem to make it work. I would appreciate some hints and / or sample code that shows what I'm doing wrong. I've checked the Cairo documentation to no avail.
The code can be found here.
And here:
#include <cairo.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
typedef struct
{
int numImages; /* Number of images in win amount string */
int indexes[7]; /* indexes into NumberImages[] */
}WinAmountData;
/***** Function Prototypes *****************/
int InitImages( void );
void DestroyNumberImages( void );
int ParseWinAmountString( char *string, WinAmountData *amtData );
int Rotate( cairo_t *cr , cairo_surface_t *image, double degrees );
/*******************************************/
typedef struct
{
int xOffset; /* pixel count offsete before next digit */
char fileName[20];
cairo_surface_t *image;
}ImageInfo;
ImageInfo NumberImages[] =
{
{ 8, "images/0.png", NULL },
{ 10, "images/1.png", NULL },
{ 10, "images/2.png", NULL },
{ 10, "images/3.png", NULL },
{ 10, "images/4.png", NULL },
{ 10, "images/5.png", NULL },
{ 10, "images/6.png", NULL },
{ 10, "images/7.png", NULL },
{ 10, "images/8.png", NULL },
{ 10, "images/9.png", NULL },
{ 7, "images/$.png", NULL },
{ 10, "images/euro.png", NULL },
{ 7, "images/pound.png", NULL },
{ 7, "images/yen.png", NULL }
};
enum { DOLLAR = 10, EURO, POUND, YEN };
#define FALSE 0
#define TRUE 1
int InitImages( void )
{
int i;
int ret = TRUE;
cairo_status_t imgStatus;
for( i = 0; i < ( sizeof( NumberImages ) / sizeof( ImageInfo ) ) && ret == TRUE; i++ )
{
NumberImages[i].image = cairo_image_surface_create_from_png( NumberImages[i].fileName );
imgStatus = cairo_surface_status(NumberImages[i].image);
ret = ( CAIRO_STATUS_SUCCESS == imgStatus );
}
return( ret );
}
void DestroyNumberImages( void )
{
int i;
for( i = 0; i < ( sizeof( NumberImages ) / sizeof( ImageInfo ) ); i++ )
{
cairo_surface_destroy(NumberImages[i].image);
}
return;
}
int ParseWinAmountString( char *string, WinAmountData *amtData )
{
int ret = TRUE;
int i = 0, len;
len = strlen( string );
if( (len > 0) && (len < 8) )
{
amtData->numImages = len;
for( i = 0; i < amtData->numImages && TRUE == ret; i++ )
{
if( string[i] >= '0' && string[i] <= '9' )
{
amtData->indexes[i] = string[i] - '0';
}
else
{
switch( string[i] )
{
case 'D':
amtData->indexes[i] = DOLLAR;
break;
case 'Y':
amtData->indexes[i] = YEN;
break;
case 'E':
amtData->indexes[i] = EURO;
break;
case 'P':
amtData->indexes[i] = POUND;
break;
default:
ret = FALSE;
break;
}
}
}
}
else
{
ret = FALSE;
}
return( ret );
}
double DegreesToRadians( double degrees )
{
return((double)((double)degrees * ( (double)M_PI/(double)180.0 )));
}
int Rotate( cairo_t *cr , cairo_surface_t *image, double degrees )
{
int ret = 0;
cairo_translate(cr, 90, 90);
cairo_rotate(cr, DegreesToRadians( degrees ));
cairo_set_source_surface(cr, image, -90, -90);
cairo_paint(cr);
return ( ret );
}
int main(int argc, char *argv[])
{
int i,x,y;
cairo_surface_t *imgWheelBg = NULL;
WinAmountData amtData;
if( argc == 2 )
{
printf("Parsing [%s]\n", argv[1]);
if ( ParseWinAmountString( argv[1], &amtData ) == TRUE )
{
printf("Amount indexes = [ ");
for( i = 0; i < amtData.numImages; i++ )
{
printf("%d ", amtData.indexes[i]);
}
printf("]\n");
}
else
{
printf("Failed to parse amount.\n");
return( 1 );
}
}
else
{
printf("Usage: %s <Amount String>\n", argv[0]);
return( 1 );
}
if( InitImages() == TRUE )
{
imgWheelBg = cairo_image_surface_create_from_png("images/blankwheel.png");
//Create the background image
cairo_surface_t *imgResult = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 180, 180);
//Create the cairo context
cairo_t *cr = cairo_create(imgResult);
//Paint empty wheel image
cairo_set_source_surface(cr, imgWheelBg, 0, 0);
cairo_paint(cr);
// At this point the wheel is painted ( blankwheel.png )
// vvvvvvvvvv THIS PART SEEMS TO BE CAUSING TROUBLES vvvvvvvvvv
// With this call the wheel DOES get rotated 90 degress. Confirmed
// by viewing the resulting PNG file.
// HOWEVER, after the Rotate() is called the numbers aren't put on the wheel.
// if I remove the Rotate() call, the wheel is drawn, not rotated, but the
// numbers are properly composited over the image.
//Rotate( cr, imgWheelBg, 90 );
// ^^^^^^^^^^^ THIS PART SEEMS TO BE CAUSING TROUBLES ^^^^^^^^^^^
/* Set drawing begin point in pixels */
x = 101;
y = 82;
/* Draw all characters in win amount string */
for( i = 0; i < amtData.numImages; i++ )
{
cairo_set_source_surface(cr, NumberImages[amtData.indexes[i]].image, x, y);
cairo_paint(cr);
x += NumberImages[i].xOffset;
}
//Destroy the cairo context and/or flush the destination image
cairo_surface_flush(imgResult);
cairo_destroy(cr);
//And write the results into a new file
cairo_surface_write_to_png(imgResult, "result.png");
// Destroy resources
cairo_surface_destroy(imgResult);
cairo_surface_destroy(imgWheelBg);
DestroyNumberImages();
printf("SUCCESS\n");
}
else
{
printf("FAILED Init Images\n");
}
return 0;
}
EDIT: The ultimate goal is to generate an image like this one programmatically and the animate it as needed in real time within a GTK application.
EDIT: With the comments from Mikhail Kozhevnikov and Uli Schlachter I was able to find a solution using this code.
Thank you very much!
I think the problem is that the transformation is applied to everything, including the number images, and they get drawn somewhere outside the picture because of this transformation. I'd suggest that the matrix be stored before applying the transformation and restored after the circle is drawn. Or would you like the numbers to rotate too?..