#include #include #include #include #include #include #include #include "grid.h" #include "options.h" #define LINE_THICKNESS 3 #define BLOCK_SIZE 32 #define MARGIN 20 typedef struct { char* data; int w, h; } img_data_t; struct mem_encode { uint8_t *buffer; size_t size; }; static void setPixel(img_data_t* img, int x, int y, char v) { //fprintf(stderr, "setPixel(img, %d, %d, %d)\n", x, y, v); assert((x < img->w) && (y < img->h)); img->data[y * img->w + x] = v; } static void setBox(img_data_t* img, int x, int y, int w, int h, char v) { //fprintf(stderr, "setBox(img, %d, %d, %d, %d, %d)\n", x, y, w, h, v); for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { setPixel(img, x + j, y + i, v); } } } static img_data_t* gridToImageData(mazegrid_t const* g) { img_data_t* result = malloc(sizeof(img_data_t)); result->w = LINE_THICKNESS + g->width * BLOCK_SIZE + 2 * MARGIN; result->h = LINE_THICKNESS + g->height * BLOCK_SIZE + 2 * MARGIN; result->data = malloc(result->w * result->h); //fprintf(stderr, "Box dimensions: %d x %d\n", result->w, result->h); // set background memset(result->data, 0, result->w * result->h); // draw basic outline setBox(result, MARGIN, MARGIN, result->w - 2 * MARGIN, LINE_THICKNESS, 1); setBox(result, MARGIN, MARGIN + LINE_THICKNESS, LINE_THICKNESS, (g->height - 1) * BLOCK_SIZE, 1); setBox(result, MARGIN, result->h - MARGIN - 1 - LINE_THICKNESS, result->w - 2 * MARGIN, LINE_THICKNESS, 1); setBox(result, result->w - MARGIN - LINE_THICKNESS, MARGIN + LINE_THICKNESS + BLOCK_SIZE, LINE_THICKNESS, (g->height - 1) * BLOCK_SIZE, 1); // partitions for (int i = 0; i < g->height; i++) { for (int j = 0; j < g->width; j++) { //fprintf(stderr, "Drawing (%d, %d) -> %d, %d\n", j, i, mazegrid_get_edge(g, j, i, EDGE_UP), mazegrid_get_edge(g, j, i, EDGE_RIGHT)); int x = MARGIN + LINE_THICKNESS + BLOCK_SIZE * j, y = result->h - MARGIN - LINE_THICKNESS - BLOCK_SIZE * i; if (i < g->height - 1) { if (mazegrid_get_edge(g, j, i, EDGE_UP) == 0) setBox(result, x - LINE_THICKNESS, y - BLOCK_SIZE, BLOCK_SIZE + LINE_THICKNESS, LINE_THICKNESS, 1); } if (j < g->width - 1) { if (mazegrid_get_edge(g, j, i, EDGE_RIGHT) == 0) setBox(result, x + BLOCK_SIZE - LINE_THICKNESS, y - BLOCK_SIZE, LINE_THICKNESS, BLOCK_SIZE, 1); } } } return result; } static void writeImageData(img_data_t const* img, png_structp png_ptr, mazeoptions_t const* options) { for (int i = 0; i < img->h; i++) { png_byte row[3 * img->w]; for (int j = 0; j < img->w; j++) { if (img->data[i * img->w + j]) { // wall color row[3 * j] = options->wall_color[0]; row[3 * j + 1] = options->wall_color[1]; row[3 * j + 2] = options->wall_color[2]; } else { // background row[3 * j] = options->background_color[0]; row[3 * j + 1] = options->background_color[1]; row[3 * j + 2] = options->background_color[2]; } } png_write_row(png_ptr, row); } } static void freeImageData(img_data_t* img) { free(img->data); free(img); } int mazemaker_maze_to_png_opt(mazegrid_t const* g, char const* filename, mazeoptions_t const* options) { int code = 1; img_data_t* img = gridToImageData(g); FILE *f = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; if (strcmp(filename, "-") == 0) { f = stdout; } else { f = fopen(filename, "wb"); if (NULL == f) { fprintf(stderr, "Could not open %s for writing.\n", filename); code = 0; goto exit; } } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fprintf(stderr, "Could not allocate PNG write struct.\n"); code = 0; goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fprintf(stderr, "Could not allocate PNG info struct.\n"); code = 0; goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { fprintf(stderr, "Error during PNG creation.\n"); code = 0; goto exit; } png_init_io(png_ptr, f); // Write header (8 bit color depth) png_set_IHDR(png_ptr, info_ptr, img->w, img->h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); writeImageData(img, png_ptr, options); png_write_end(png_ptr, NULL); exit: if (strcmp(filename, "-") != 0) { if (f != NULL) fclose(f); } if (info_ptr != NULL) { png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_info_struct(png_ptr, &info_ptr); } if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); if (img != NULL) freeImageData(img); return code; } int mazemaker_maze_to_png(mazegrid_t const* g, char const* filename) { mazeoptions_t* options = mazemaker_options_new(); // use default options int result = mazemaker_maze_to_png_opt(g, filename, options); mazemaker_options_free(options); return result; } static void append_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { struct mem_encode* p = (struct mem_encode*)png_get_io_ptr(png_ptr); size_t nsize = p->size + length; if (p->buffer) p->buffer = realloc(p->buffer, nsize); else p->buffer = malloc(nsize); if (!p->buffer) png_error(png_ptr, "Write error"); memcpy(p->buffer + p->size, data, length); p->size += length; } static void png_no_flush(png_structp png_ptr) { /* noop */ } int mazemaker_maze_to_png_mem_opt(mazegrid_t const* g, size_t* len, uint8_t** buf, mazeoptions_t const* options) { int code = 1; img_data_t* img = gridToImageData(g); png_structp png_ptr = NULL; png_infop info_ptr = NULL; struct mem_encode menc = { .buffer = NULL, .size = 0 }; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fprintf(stderr, "Could not allocate PNG write struct.\n"); code = 0; goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fprintf(stderr, "Could not allocate PNG info struct.\n"); code = 0; goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { fprintf(stderr, "Error during PNG creation.\n"); code = 0; goto exit; } png_set_write_fn(png_ptr, &menc, append_png_data, png_no_flush); // Write header (8 bit color depth) png_set_IHDR(png_ptr, info_ptr, img->w, img->h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); writeImageData(img, png_ptr, options); png_write_end(png_ptr, NULL); *len = menc.size; *buf = menc.buffer; exit: if (!code && menc.buffer) { free(menc.buffer); } if (info_ptr != NULL) { png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); png_destroy_info_struct(png_ptr, &info_ptr); } if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); if (img != NULL) freeImageData(img); return code; } int mazemaker_maze_to_png_mem(mazegrid_t const* g, size_t* len, uint8_t** buf) { mazeoptions_t* options = mazemaker_options_new(); // use default options int result = mazemaker_maze_to_png_mem_opt(g, len, buf, options); mazemaker_options_free(options); return result; }