diff --git a/include/mazemaker.h b/include/mazemaker.h index 05a61a6..20b2f32 100644 --- a/include/mazemaker.h +++ b/include/mazemaker.h @@ -25,5 +25,6 @@ typedef enum { void mazemaker_generate_maze(int width, int height, mazegrid_t* result); void mazemaker_free_maze(mazegrid_t* maze); int mazemaker_maze_to_png(mazegrid_t const* maze, char const* filename); +int mazemaker_maze_to_png_mem(mazegrid_t const* maze, size_t* len, uint8_t** buf); #endif // !def(_MAZEMAKER_H) diff --git a/lib/image.c b/lib/image.c index 17a7b6b..c57d0a0 100644 --- a/lib/image.c +++ b/lib/image.c @@ -16,6 +16,11 @@ typedef struct { 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)); @@ -150,3 +155,76 @@ exit: if (img != NULL) freeImageData(img); return code; } + +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(mazegrid_t const* g, size_t* len, uint8_t** buf) { + 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); + + 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; +}