I am currently making a sdl2(/graphical) version of the game of life in C, so when the screen is resized I need to update the grid. When the screen is resized I don't want it to just change the size of the tiles, I want it to actually create more tiles. So I reallocate the cells
list, which contains all of the cells states but for some odd reason that doesn't work.
bool updateCells(int w, int h) {
size_t x, y,
oldGridCol,
oldGridRow;
oldGridCol = gridCol;
oldGridRow = gridRow;
gridRow = w / gridSizeW;
gridCol = h / gridSizeH;
cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
if(cells == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory allocation failed!");
return false;
}
for(y = 0; y < oldGridRow; y++) {
for(x = 0; x < oldGridCol; x++) {
writeCell(y, x, *(cells + (x + (y * oldGridCol))));
}
}
return false;
}
When this function is called the realloc
function returns this:
malloc(): invalid size (unsorted)
Aborted
Thank you in advance!
I have made a minimal reproducible example of this program, which doesn't use SDL2, just plain C. Now for some reason this example works.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
typedef enum cellState {
dead,
live,
potentialDead,
potentialLive
} cellState_t;
size_t gridRow,
gridCol,
gridSizeW,
gridSizeH;
cellState_t *cells;
bool quitLoop;
bool initCells(void);
bool updateCells(int w, int h);
void writeCell(size_t row, size_t col, cellState_t state);
cellState_t readCell(size_t row, size_t col);
void die(const char *f, const size_t l, const char *fmt, ...);
int main(int argc, char *args[]) {
int w, h;
quitLoop = false;
gridSizeW = 25;
gridSizeH = 25;
// Let's assume that the window size is 640 by 480
gridRow = 640 / gridSizeW;
gridCol = 480 / gridSizeH;
if(!initCells())
die(__FILE__, __LINE__, "Failed to initialize cells!");
writeCell(1, 2, live);
writeCell(2, 3, live);
writeCell(3, 3, live);
writeCell(3, 2, live);
writeCell(3, 1, live);
while(!quitLoop) {
updateCells(640, 480);
printf("%d\n", readCell(1, 2));
}
return EXIT_SUCCESS;
}
bool initCells(void) {
cells = calloc((gridRow * gridCol), sizeof(cellState_t));
if(cells == NULL) {
return false;
}
return true;
}
bool updateCells(int w, int h) {
size_t x, y,
oldGridCol,
oldGridRow;
oldGridCol = gridCol;
oldGridRow = gridRow;
gridRow = w / gridSizeW;
gridCol = h / gridSizeH;
cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
if(cells == NULL) {
return false;
}
for(y = 0; y < oldGridRow; y++) {
for(x = 0; x < oldGridCol; x++) {
writeCell(y, x, *(cells + (x + (y * oldGridCol))));
}
}
return false;
}
void writeCell(size_t row, size_t col, cellState_t state) {
*(cells + (col + (row * gridCol))) = state;
}
cellState_t readCell(size_t row, size_t col) {
return *(cells + (col + (row * gridCol)));
}
void die(const char *f, const size_t l, const char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt);
fprintf(stderr, "error from file %s on line %ld: ", f, l);
//SDL_LogMessageV(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_CRITICAL, fmt, vargs);
fputc('\n', stderr);
va_end(vargs);
exit(EXIT_FAILURE);
}
Maybe it's the window size variable that affects the outcome, or something like that.
But the full code doesn't work, here's the full code:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define WINDOW_NAME "sdl-life"
#define WINDOWW 640
#define WINDOWH 480
typedef enum cellState {
dead,
live,
potentialDead,
potentialLive
} cellState_t;
SDL_Window *gWindow;
SDL_Renderer *gRenderer;
SDL_Texture *gLiveCellTexture,
*gGrid;
size_t gridRow,
gridCol,
gridSizeW,
gridSizeH;
cellState_t *cells;
bool quitLoop;
bool initSdl(void);
void closeSdl(void);
SDL_Texture *loadTexture(const char *path);
bool loadMedia(void);
bool initCells(void);
bool updateCells(int w, int h);
void writeCell(size_t row, size_t col, cellState_t state);
cellState_t readCell(size_t row, size_t col);
void displayCell(cellState_t status, SDL_Rect location);
void displayAllCells(void);
void die(const char *f, const size_t l, const char *fmt, ...);
int main(int argc, char *args[]) {
SDL_Event event;
int w, h;
quitLoop = false;
if(!initSdl())
die(__FILE__, __LINE__, "Failed to initialize SDL!");
if(!loadMedia())
die(__FILE__, __LINE__, "Failed to load media!");
SDL_GetWindowSize(gWindow, &w, &h);
gridSizeW = 25;
gridSizeH = 25;
gridRow = w / gridSizeW;
gridCol = h / gridSizeH;
if(!initCells())
die(__FILE__, __LINE__, "Failed to initialize cells!");
writeCell(1, 2, live);
writeCell(2, 3, live);
writeCell(3, 3, live);
writeCell(3, 2, live);
writeCell(3, 1, live);
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);
while(!quitLoop) {
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT)
quitLoop = true;
}
SDL_RenderClear(gRenderer);
SDL_GetWindowSize(gWindow, &w, &h);
updateCells(w, h);
displayAllCells();
SDL_RenderPresent(gRenderer);
}
closeSdl();
return EXIT_SUCCESS;
}
bool initSdl(void) {
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "The initialization process has begun");
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL: %s", SDL_GetError());
return false;
}
if(!IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_image: %s", IMG_GetError());
return false;
}
if(TTF_Init() == -1) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_ttf: %s", TTF_GetError());
return false;
}
gWindow = SDL_CreateWindow(WINDOW_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOWW, WINDOWH, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if(gWindow == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to create the window: %s", SDL_GetError());
return false;
}
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if(gRenderer == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to create the renderer: %s", SDL_GetError());
return false;
}
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "The initialization has finished");
return true;
}
void closeSdl(void) {
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "SDL is shutting DOWN!");
free(cells);
SDL_DestroyTexture(gLiveCellTexture);
SDL_DestroyTexture(gGrid);
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
IMG_Quit();
TTF_Quit();
SDL_Quit();
}
SDL_Texture *loadTexture(const char *path) {
SDL_Texture *newTexture;
SDL_Surface *loadedSurface;
loadedSurface = IMG_Load(path);
if(loadedSurface == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
return NULL;
}
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0x0, 0xff, 0xff));
newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
if(newTexture == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to convert surface to texture: %s", SDL_GetError());
return NULL;
}
SDL_FreeSurface(loadedSurface);
return(newTexture);
}
bool loadMedia(void) {
gLiveCellTexture = loadTexture("livecell.png");
if(gLiveCellTexture == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
return false;
}
gGrid = loadTexture("grid.png");
if(gGrid == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Failed to load surface: %s", IMG_GetError());
return false;
}
return true;
}
bool initCells(void) {
cells = calloc((gridRow * gridCol), sizeof(cellState_t));
if(cells == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory allocation failed!");
return false;
}
return true;
}
bool updateCells(int w, int h) {
size_t x, y,
oldGridCol,
oldGridRow;
oldGridCol = gridCol;
oldGridRow = gridRow;
gridRow = w / gridSizeW;
gridCol = h / gridSizeH;
cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
if(cells == NULL) {
SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Memory reallocation failed!");
return false;
}
for(y = 0; y < oldGridRow; y++) {
for(x = 0; x < oldGridCol; x++) {
writeCell(y, x, *(cells + (x + (y * oldGridCol))));
}
}
return false;
}
void writeCell(size_t row, size_t col, cellState_t state) {
*(cells + (col + (row * gridCol))) = state;
}
cellState_t readCell(size_t row, size_t col) {
return *(cells + (col + (row * gridCol)));
}
void displayCell(cellState_t status, SDL_Rect location) {
SDL_RenderCopy(gRenderer, gGrid, NULL, &location);
if(status == live) {
SDL_RenderCopy(gRenderer, gLiveCellTexture, NULL, &location);
}
}
void displayAllCells(void) {
size_t x, y;
SDL_Rect location;
location.w = gridSizeW;
location.h = gridSizeH;
location.x = 0;
location.y = 0;
for(y = 0; y < gridRow; y++) {
for(x = 0; x < gridCol; x++) {
displayCell(readCell(y, x), location);
location.x += location.w;
}
location.y += location.h;
location.x = 0;
}
}
void die(const char *f, const size_t l, const char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt);
fprintf(stderr, "error from file %s on line %ld: ", f, l);
SDL_LogMessageV(SDL_LOG_CATEGORY_ERROR, SDL_LOG_PRIORITY_CRITICAL, fmt, vargs);
va_end(vargs);
closeSdl();
exit(EXIT_FAILURE);
}
My guess is that your problem occurs when you try to realloc cells to
gridRow * gridCol
since w / gridSizeW seems to be the amount of elements you want. Try:
cells = (cellState_t *)realloc(cells, (gridRow * gridCol) * sizeof(cellState_t));
Let's analize your code. The grid is a 2D-array named cells, which you allocate dynamically basing on its dimensions which are kept in gridSizeW and gridSizeH.
Ideally to address a cell you use its coordinates which would go from (1 to gridSizeW; 1 to gridSizeH) or instead, more commonly, from (0 to gridSizeW-1; 0 to gridSizeH-1).
Your routine to get the value of a cell is:
cellState_t readCell(size_t row, size_t col) {
return *(cells + (col + (row * gridCol)));
}
which implements the idea of taking the requested row number, multiply it for the width, and adding the column number. This is correct to turn a 2D address to a 1D one.
The problem is that your in-memory representation of the grid has nothing (or little) to do with the dimensions of the screen. If you have a grid 25 x 25 (you assign these as initial width and height), you must allocate 25*25 cells , no matter what SDL_GetWindowSize() returns to you.
Basically, you should always use gridSizeW and gridSizeH for coping with the cell pointer. You use instead gridRow and gridCol . Only when you update the screen, you perform a "scaling" of a row+column coordinate of then grid to an X+Y coordinate of the screen. So your readcell routine, assuming a 0-based numbering, should be:
cellState_t readCell(size_t row, size_t col) {
return *(cells + (col + (row * gridSizeW)));
// gridSizeW -------^
}
The variables gridRow and gridCol, which are the ratio of w/gridwidth and h/gridheight as you correctly wrote:
SDL_GetWindowSize(gWindow, &w, &h); // w+h: screen dimensions
gridSizeW = 25; // grid dimensions
gridSizeH = 25;
gridRow = w / gridSizeW; // screenwidth=250? then a cell is 10 pixels wide
gridCol = h / gridSizeH;
are to be used only when interfacing to SDL.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.