SDL - Blitting an Array of SDL_Rect - c

Having trouble blitting an image in SDL2. I built an array of SDL_Rect using malloc and attributed to each member the usual x,y,w (width), and h (height) variables, but the images simply won't show up. Curiously, when I try blitting outside of a for loop by directly referencing the index of the element, it seems to work. Any idea what's going on?
//Not Working
int number =5;
for(int i = 0; i < number; i++){
SDL_BlitScaled(myImage, NULL, mySurface, &(myElements[i]));
}
//Works Perfectly
SDL_BlitScaled(myImage, NULL, mySurface, &(myElements[0]));
Edit: Ended up forgetting this part. Here's how the array is being generated:
SDL_Rect *myElements;
myElements = (SDL_Rect * ) malloc (number * sizeof(SDL_Rect));
generateGroup(myElements, number, 10, 10, 180, 30);
The function I am calling there looks like this:
void generateGroup(SDL_Rect* elementArray, int numberElements, int posX, int posY, int width, int height) {
int i;
for(i = 0; i < numberElements; i++) {
elementArray[i].x = posX;
posX = posX + width + 1;
elementArray[i].y = posY;
elementArray[i].w = width;
elementArray[i].h = height;
}
}
To describe the issue more precisely, the image literally only appears when I blit outside the for loop. As for the index, in addition to 0, I have also tried 1 and 2, and both worked.

Related

C - How to check if at least one neighbor of a cell in a matrix respects a predication?

I'm on a project where I have to code a little game named "recolor", basically it's grid of a certain size, the grid is divided in cells (we have size*size cells), I can play a color (the color have to be in the grid or in the authorized colors) and each time I'm playing a color the grid will change from the top letf corner to color each cells directly connected to the top left corner that has the same color.
That's for the global context, now I already have all the functions to play this game but I have some problems with the function I use to play a move:
The game is defined using this type :
struct game_s
{
color **grid;
color **initial_grid;
uint width;
uint height;
uint nmb_cur_mv;
uint nmb_mv_max;
};
/*
* Def :
* - neighbour_of_origin checks if a cell is in the neighborhood of the (0,0) of the game grid, to be in the neighborhood
* the cell should have the same color as the (0,0) and the cell and (0,0) have to be connected by a path of cells that have the same
* color as (0,0).
* Param :
* - cgame g : the game grid
* - int x, y : coordinates of the cell we are currently checking
* - bool** to_change_tab : 2D array of boolean that represent the game grid and have the same size as it. It is used to mark all the cells to
* modify. By default the whole grid is set to false except (0,0) which is changed no matter what.
* Ret :
* - void function, nothing to return.
*/
void neighbour_of_origin(cgame g, int x, int y, bool **to_change_tab)
{
assert(g);
assert(g->grid);
assert(to_change_tab);
color origin = g->grid[0][0];
int offset[4][2] = {
{-1, 0}, /*W*/
{0, 1}, /*S*/
{1, 0}, /*E*/
{0, -1} /*N*/
};
int off_x;
int off_y;
for (int elem = 0; elem < 4; elem++)
{
off_x = x + offset[elem][0]; /*takes the 'x' coord in the offset matrix and add it to the current x*/
off_y = y + offset[elem][1]; /*takes the 'y' coord in the offset matrix and add it to the current y*/
if (((off_x >= 0 && off_x < SIZE) && (off_y >= 0 && off_y < SIZE)) && (to_change_tab[off_x][off_y] == true) && (g->grid[x][y] == origin))
{
to_change_tab[x][y] = true;
break;
}
}
}
/*
* Def :
* - game_play_one_move will change all the cells that are neighbors of the (0,0)that have the same color as it. The changing always begin
from the (0,0) and then it expands in all directions.
* Param :
* - game g : the game grid that will be modified.
* - color c : the color that will be applied to all the cells we have to change.
* Ret :
* - void function, nothing to return.
*/
void game_play_one_move(game g, color c)
{
assert(g);
assert(g->grid);
bool **to_change_tab = (bool **)malloc(sizeof(bool *) * SIZE);
assert(to_change_tab);
for (int i = 0; i < SIZE; i++)
{
to_change_tab[i] = (bool *)malloc(sizeof(bool) * SIZE);
assert(to_change_tab[i]);
for (int j = 0; j < SIZE; j++)
{
to_change_tab[i][j] = false;
}
}
/*getting all the cells we have to change*/
to_change_tab[0][0] = true; /*(0,0) is always modified */
printf("mark tab : \n");
for (int x = 0; x < SIZE; x++)
{
for (int y = 0; y < SIZE; y++)
{
neighbour_of_origin(g, x, y, to_change_tab);
printf("%d ", to_change_tab[x][y]); /*debbuging purpose*/
}
printf("\n");
}
/*modification of the game grid */
for (int x = 0; x < SIZE; x++)
{
for (int y = 0; y < SIZE; y++)
{
if (to_change_tab[x][y] == true)
{
g->grid[x][y] = c;
}
}
}
g->nmb_cur_mv += 1;
/*free the temporary array of bools*/
for (int x = 0; x < SIZE; x++)
{
free(to_change_tab[x]);
to_change_tab[x] = NULL;
}
free(to_change_tab);
to_change_tab = NULL;
}
above we have the two functions I've made to change the color as described in the rules, It actually works pretty well but I have a big issue on it.
This is my grid at the very begining :
each number corresponds to a color, the grid is 12*12 cells.
000202101030
033111132010
101232320332
231032111220
212333320100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
Here is my grid when I play the following colors : 3 1 3 0
000202101030
000000002010
000202020332
230002111220
212300020100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
if I play any color (except 0 of course) as my next move I expect every 0 connected to the top left to change but i got this instead:
333202101030
333333332010
333232320332
233332111220
212333320100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
the 0 on the first line didn't changed whereas they were connected to the origin, I suspected that the bug is in my condition in neighbour_of_origin(cgame g, int x, int y, bool **to_change_tab) because i'm using a for on x and y so the first line is evaluated first and the 0 on the first line did not have any neighbor that corresponds to the if condition at this state, is there a way to evaluate in "the order of propagation",I wanted to rewrite it recursively but I'm really bad in recursion.
Very sorry for the long post, I wish someone could help me, have a good day.
edit : I got a ''''fix'''' I duplicate my for loops in the game_play_one_move() function so it will loop again and again to find each cells he couldn't find before, I know it's not a real fix because in the future i don't know how the grid will be displayed exactly, but i guess that a loop that will check again and again until it can't find any neighbor could be a solution too, in terms of colplexity it would be a real problem since i'm looping in the grid again and again and again

Difficulties getting a pure sounding sine wave (currently getting a subtle frequency with it)

I'm attempting to play a pure sine wave in SDL2, but I'm finding that I can't seem to get a completely pure tone. It sounds pretty close to a true sine wave, but there is a slight secondary frequency behind it that sounds closer to a square wave. I've recorded the sound and verified that it is indeed incorrect when playing through two sets of speakers on two different systems (compared to a pure sine wave)
I've tried quite a few things at this point, including implementing multiple sine waves from stack overflow, and attempting to adapt the code from Handmade Hero. But each time, the same problem crops up. My suspicion is that there's something wrong with the bit precision, an incorrect cast somewhere, or that it has something to do with the specific way that SDL audio works that I'm not navigating around properly
Here's the main audio callback function that I'm currently working with along with my most recent attempt at writing a sine wave to the buffer:
#define Pi32 3.14159265359f
#define Tau32 (2.0f * Pi32)
void
AudioCallback(void* UserData, u8* Stream, int Length)
{
audio_user_data* AudioUserData = (audio_user_data*)UserData;
static u32 Count = 0;
u16* SampleBuffer = (u16*)Stream;
int SamplesToWrite = Length / AudioUserData->BytesPerSample;
for(int SampleIndex = 0; SampleIndex < SamplesToWrite; SampleIndex++)
{
u16 ToneValue = round((AudioUserData->ToneVolume * sin(Tau32 * (f32)Count / (f32)AudioUserData->WavePeriod)));
*SampleBuffer++ = ToneValue;
*SampleBuffer++ = ToneValue;
++Count;
}
}
I would be happy to provide more context if it might help
EDIT -- Additional Context:
#include "x:\SDL2-2.0.10\include\SDL.h"
#define Pi32 3.14159265359f
#define Tau32 (2.0f * Pi32)
#define INITIAL_SCREEN_WIDTH (8*80)
#define INITIAL_SCREEN_HEIGHT (8*60)
typedef struct audio_user_data audio_user_data;
struct audio_user_data
{
int SamplesPerSecond;
int BytesPerSample;
int SampleIndex;
int ToneHz;
int ToneVolume;
int WavePeriod;
u32 FileLength;
u16* BufferLocation;
};
void
AudioCallback(void* UserData, u8* Stream, int Length)
{
audio_user_data* AudioUserData = (audio_user_data*)UserData;
static u32 Count = 0;
u16* SampleBuffer = (u16*)Stream;
int SamplesToWrite = Length / AudioUserData->BytesPerSample;
for(int SampleIndex = 0; SampleIndex < SamplesToWrite; SampleIndex++)
{
u16 ToneValue = (0.5f + (AudioUserData->ToneVolume * sin(Tau32 * Count / AudioUserData->WavePeriod)));
*SampleBuffer++ = ToneValue;
*SampleBuffer++ = ToneValue;
++Count;
}
}
int
main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO);
SDL_Window* Window = SDL_CreateWindow("Spell Checker", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, INITIAL_SCREEN_WIDTH*2, INITIAL_SCREEN_HEIGHT*2, 0);
SDL_Renderer* Renderer = SDL_CreateRenderer(Window, 0, SDL_RENDERER_SOFTWARE);
SDL_PixelFormat* Format = SDL_AllocFormat(SDL_PIXELFORMAT_RGB888);
SDL_Texture* Screen = SDL_CreateTexture(Renderer, Format->format, SDL_TEXTUREACCESS_STREAMING, INITIAL_SCREEN_WIDTH, INITIAL_SCREEN_HEIGHT);
audio_user_data AudioUserData = {0};
AudioUserData.SamplesPerSecond = 44100;
AudioUserData.BytesPerSample = 2 * sizeof(int16);
AudioUserData.SampleIndex = 0;
AudioUserData.ToneVolume = 3000;
AudioUserData.ToneHz = 440;
AudioUserData.WavePeriod = AudioUserData.SamplesPerSecond / AudioUserData.ToneHz;
SDL_AudioSpec Want, Have;
SDL_AudioDeviceID AudioDeviceID;
Want.freq = AudioUserData.SamplesPerSecond;
Want.format = AUDIO_S16;
Want.channels = 2;
Want.samples = 4096;
Want.callback = &AudioCallback;
Want.userdata = &AudioUserData;
AudioDeviceID = SDL_OpenAudioDevice(0, 0, &Want, &Have, 0);
SDL_PauseAudioDevice(AudioDeviceID, 0); // Start playing
u32* PixelMap = calloc(INITIAL_SCREEN_WIDTH * INITIAL_SCREEN_HEIGHT, sizeof(PixelMap));
int PixelMapLocation = 0;
int Running = 1;
while(Running)
{
SDL_Event Event;
while(SDL_PollEvent(&Event))
{
if(Event.type == SDL_QUIT)
{
Running = 0;
break;
}
}
// Test colors
PixelMapLocation = 0;
for(int Row = 0; Row < INITIAL_SCREEN_WIDTH; ++Row)
{
for(int Col = 0; Col < INITIAL_SCREEN_HEIGHT; ++Col)
{
PixelMap[PixelMapLocation++] = 0xFF00FF;
}
}
for(int Row = 0; Row < INITIAL_SCREEN_WIDTH; ++Row)
{
for(int Col = 0; Col < INITIAL_SCREEN_HEIGHT; ++Col)
{
PixelMap[PixelMapLocation++] = 0x00FFFF;
}
}
SDL_UpdateTexture(Screen, 0, PixelMap, INITIAL_SCREEN_WIDTH * sizeof(PixelMap));
SDL_RenderClear(Renderer);
SDL_RenderCopy(Renderer, Screen, 0, 0);
SDL_RenderPresent(Renderer);
}
return(0);
}
EDIT2:
I recorded the audio I'm hearing here (might need to turn the volume up to hear the problem):
https://www.youtube.com/watch?v=-V2IMhK2Zis&feature=youtu.be
I also ran a timing test, and got this back for the each run through of the AudioCallback function:
https://imgur.com/a/9pqCte0
EDIT3:
Oscilloscope readings --
My sine wave:
A pure sine wave:
(I can't see an appreciable difference, but maybe someone else can?) Edit: Oh wait, on the left side of the oscilloscope, there are clear differences between the two waveforms (though they don't appear in the main reading). Trying to figure out what the issue is -- at this point I am still unsure since I have tried several different algorithms
EDIT4:
Here's a picture of Want / Have to show that everything (other than size) is the same after calling SDL_OpenAudioDevice:
EDIT5:
Problem (sort of) solved! Setting AudioUserData.SamplesPerSecond to 48000 resulted in a pure sounding sine wave. But the question still stands: why does it only work at 48000??

Point in polygon - faulty algorithm

I have this C code of an alogrithm checking whether a given point is inside a polygon. It is supposed to be correct, I also keep seeing this code in various places. However when I use it doesn't work perfectly - about 20% of the answers are wrong.
int pnpoly(int nvert, double *vertx, double *verty, double testx, double testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
Maybe there is something wrong with my main function. Could somone give me a main function to check this algorthm?
This is my main function
int main(){
double vertx[4] = {10, 10, 0, 0};
double verty[4] = {10, 0, 10, 0};
// for those two it returns "Inside"
double testx = 6;
double testy = 4;
/* for those two it returns "Outside"
double testx = 5;
double testy = 4;
*/
int result = pnpoly(4, vertx, verty, testx, testy);
if (result) {
printf("\nInside\n");
}
else {
printf("\nOutside\n");
}
return 0;
}
Your polygon is self intersecting. It's normal that (5,4) is "Outside"
I think you thought that your polygon was a square, the algo works perfectly even with self intersecting polygons.

C 2D Array rotating

I have a 2D pointer setup representing a grid, the grid consists of columns containing 1/0 or null columns (i.e. don't contain 1 in any cell). This function spins the grid by 90deg clockwise, and works except...
I think my malloc could be wrong as it works but I get many over picket-fence errors in dmalloc.
Am I allocating incorrect amounts of memory?
Also I wanted to swap the values of *width and *height to represent the new width and height of the grid but when I try this the program just segfaults on the second spin.
So *width is the dimension of orig's first dimension, so it should be the size of newg's second dimension.
Similarly *height should be the size of newg's first, and hence the two sets of malloc sizes have been flipped the wrong way around.
I think it would be clearer to name the values orig_max_x and orig_max_y, then it should be clear if the function uses the values the wrong way around.
newg = malloc (*height * sizeof(char *));
// Initialise each column
for (x = 0; x < *height; x++) {
newg[x] = malloc (*width);
for (y = 0; y < *width; y++)
newg[x][y] = 0;
}
Further, it should not free any of newg's storage if you want to return values from spin()
Edit: I still had some of those pesky *width and *height mixed. Sorry.
I strongly suggest the names should relate to the thing they talk about, orig_width,
orig_height would be have helped me read the code.
This is probably how I'd do it:
#include <stdio.h>
#include <stdlib.h>
char** alloc_rectangle(int *width, int *height);
void free_rectangle(char **orig, int *width);
char** spin (char **orig, int *width, int *height);
int main (int argc, const char * argv[]) {
int width = 20;
int height = 30;
char** orig = alloc_rectangle(&width, &height);
char** newg = spin(orig, &width, &height);
return 0;
}
char** alloc_rectangle(int *width, int *height)
{
char **newg = calloc (*width, sizeof(char *));
// Initialise each column
for (int x = 0; x < *width; x++) {
newg[x] = calloc (*height, sizeof(char));
}
return newg;
}
void free_rectangle(char **orig, int *width)
{
// free memory for old grid
for (int x = 0; x < *width; x++) {
if (orig[x] != NULL) {
free (orig[x]);
}
}
free (orig);
}
char** spin (char **orig, int *width, int *height)
{
int x;
int y;
char **newg = alloc_rectangle(height, width);
// Rotate
for (x = 0; x < *width; x++) {
for (y = 0; y < *height; y++)
if (orig[x] != NULL)
newg[*height - 1 - y][x] = orig[x][y];
}
return newg;
}
WARNING Untested code - some fun for all :-)
I don't think it is spin's job to free orig. I'd prefer it to just make space to hold the result of spinning. So to make things tidier, I pulled freeing a rectangle into its own function. Similarly, I'd always want the rectangles to be allocated consistently, so that would be its own function.
Take another look at the code that rotates the grid. I don't think you ever want to mix x and y coordinates, so an index like *width - 1 - y looks suspicious. For example, suppose *width = 3 and *height = 5. Then y ranges from 0 to 4, and you can end up with newg[3 - 1 - 4] = newg[-2].
Also, if you've allocated orig the same way you allocated newg you'll need to free it like this:
for (x=0; x < *width; x++) {
free (orig[x]); // Free the individual columns
}
free (orig); // Free the array of pointers.
I just wrote this up quickly and it seemed to work fine with the few tests I ran on it.
char **rotate(char **original, int *width, int *height)
{
int t_width = *height;
int t_height = *width;
char **newgrid = (char**)calloc(t_height, sizeof(char*));
for(int y = 0; y < t_height; y++)
{
newgrid[y] = (char*)calloc(t_width, sizeof(char));
for(int x = 0; x < t_width; x++)
newgrid[y][x] = original[x][y];
}
for(int y = 0; y < *height; y++)
free(original[y]);
free(original);
*width = t_width;
*height = t_height;
return newgrid;
}
Let me know if there are any problems.

Simple problem moving enemy in game (C / SDL)

I'm hacking away at a simple game to teach myself C and I've come up against an infuriatingly simple problem that I haven't been able to Google an answer to.
Code follows, apologies for its noobie terribleness (criticisms appreciated!):
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#define AMOUNT_OF_ENEMIES 10
#define AMOUNT_OF_PIXELS_TO_MOVE 50.0
struct enemy
{
int alive;
SDL_Rect rect;
};
void create_enemy(struct enemy *position)
{
// Take a pointer to an array. Iterate through array looking for any 'dead' instances.
// (Re)initialise when found, ignore entirely if array is full of alive instances.
int j = 0;
while(position[j].alive == 1 && j < AMOUNT_OF_ENEMIES)
{
++j;
}
if(position[j].alive == 0)
{
position[j].alive = 1;
position[j].rect.y = 0;
}
}
void update_enemies(struct enemy *position)
{
// Iterate through a passed array looking for alive instances. If found increment vertical position,
// unless instance is at bottom of screen in which case it's marked as dead.
int j = 0;
while(j < AMOUNT_OF_ENEMIES)
{
if(position[j].alive == 1)
{
position[j].rect.y += 1;
if(position[j].rect.y > 570)
{
position[j].alive = 0;
}
}
++j;
}
}
int main(void)
{
// INITS *********************************************************************
int k;
int current_time = 0;
int previous_time = 0;
float difference_in_time = 0.0;
// Load SDL library
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Problem, yo\n");
return 1;
}
// Setup event queue
SDL_Event event;
// Create array to store enemys, initialise it
struct enemy *enemy_array = malloc(sizeof(struct enemy) * AMOUNT_OF_ENEMIES);
int j;
for(j = 0; j < AMOUNT_OF_ENEMIES; ++j)
{
enemy_array[j].alive = 0;
enemy_array[j].rect.x = 150;
enemy_array[j].rect.y = 0;
}
// Create an array to flag keypresses, initialise it
int pressed_keys[323];
int l;
for(l = 0; l < 323; ++l)
{
pressed_keys[l] = 0;
}
// Create surfaces
SDL_Surface *screen = SDL_SetVideoMode(300, 600, 0, SDL_HWSURFACE);
int black = SDL_MapRGB(screen->format, 0, 0, 0);
SDL_Surface *tower = SDL_LoadBMP("tower.bmp");
SDL_Rect tower_rect;
tower_rect.x = 50;
tower_rect.y = 0;
tower_rect.w = 200;
tower_rect.h = 600;
SDL_Surface *dude = SDL_LoadBMP("dude.bmp");
float dude_x = 0.0;
SDL_Rect dude_rect;
dude_rect.x = 120;
dude_rect.y = 500;
dude_rect.w = 60;
dude_rect.h = 100;
SDL_Surface *enemy = SDL_LoadBMP("enemy.bmp");
// GAME LOOP *****************************************************************
while(1)
{
current_time = SDL_GetTicks();
difference_in_time = (float)(current_time - previous_time) / 1000;
previous_time = current_time;
if(SDL_PollEvent(&event))
{
if(event.key.keysym.sym == SDLK_DOWN)
{
create_enemy(enemy_array);
}
else
{
switch(event.type)
{
case SDL_QUIT:
printf("NOOOOOO\n");
SDL_FreeSurface(screen);
SDL_FreeSurface(tower);
SDL_FreeSurface(enemy);
free(enemy_array);
SDL_Quit();
return 0;
case SDL_KEYDOWN:
pressed_keys[event.key.keysym.sym] = 1;
break;
case SDL_KEYUP:
pressed_keys[event.key.keysym.sym] = 0;
break;
}
}
}
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
update_enemies(enemy_array);
SDL_FillRect(screen, NULL, black);
SDL_BlitSurface(tower, NULL, screen, &tower_rect);
for(k = 0; k < AMOUNT_OF_ENEMIES; ++k)
{
if(enemy_array[k].alive == 1)
{
SDL_BlitSurface(enemy, NULL, screen, &enemy_array[k].rect);
}
}
SDL_BlitSurface(dude, NULL, screen, &dude_rect);
SDL_Flip(screen);
}
return 0;
}
The issue arises at this part:
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
The 'dude' object moves to the left correctly, but nothing happens when the right arrow key is pressed.
Adding a printf tells me the if statement is being executed correctly. Removing difference_in_time makes it work, so it's either something to do with that variable or the operation of it and AMOUNT_OF_PIXELS_TO_MOVE.
I just can't for the life of me figure out why the former block executes correctly and the latter (which is essentially the same thing) doesn't. I'm sure it's something simple I've overlooked but I'm going insane trying to find it.
Your problem is due to rounding.
For your "dude" you are using a SDL_Rect, that uses integer coordinates (short int if I remember correct).
You configured your dude speed to 50 and if your game is running at 60fps (probably due to its simplicity and it may be much more if vsync is off) you will get each frame a movement value of 0.83333.
This value will be truncated to a int and the result will be zero, for example, if dude.x is 10 and you press right, the calculated value will be 10.83 and when truncated this will result in 10.
For left, it works because the value is rounded down, assuming again dude.x is 10, when left is pressed, on the first iteration the calculated value would be 9.17, truncating this will give you 9.
Simple, bad and Hack Solution
Increase AMOUNT_OF_PIXELS_TO_MOVE to a higher value that forces the int to increase, this will fix the problem.
Good Solution
Does not use SDL_Rect for storing your characters position, create a "MyRect" and use float values in it and only does rounding when drawing the character. Actually you only need to store the character position, so I would create a Point2D struct with only x and y and use this to keep track of characters position.

Resources