#include #include #include #include #include #include #include "image.h" #define LINE_THICKNESS 3 #define BLOCK_SIZE 32 #define MARGIN 20 typedef struct { char* data; int w, h; } img_data_t; 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); // white 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) { 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]) { // black row[3 * j] = row[3 * j + 1] = row[3 * j + 2] = 0; } else { // white row[3 * j] = row[3 * j + 1] = row[3 * j + 2] = 255; } } png_write_row(png_ptr, row); } } static void freeImageData(img_data_t* img) { free(img->data); free(img); } int writePNG(mazegrid_t const* g, char const* filename) { 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); 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; }