X-Git-Url: https://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/e7aea0c6f323c6bbf364093305e60bb78999a23f..4ec9360786d0bb19cffc76818c1a4c33043f68e1:/src/png.c diff --git a/src/png.c b/src/png.c index ee5d0a5..288cf77 100644 --- a/src/png.c +++ b/src/png.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -135,8 +136,47 @@ write_rgba_frame(png_structp png, png_infop info, } static void -write_index_frame(png_structp png, png_infop info, unsigned char **framedata, - struct lbx_colour *palette) +write_masked_index_frame(png_structp png, png_infop info, + unsigned char **framedata, unsigned char **mask) +{ + png_uint_32 width = png_get_image_width(png, info); + png_uint_32 height = png_get_image_height(png, info); + unsigned char (*row)[2]; + jmp_buf parent; + + if (width >= PNG_UINT_32_MAX / sizeof *row) + fatal(png, -1, "image too wide to allocate row buffer"); + + row = png_malloc(png, width * sizeof *row); + + /* + * We need to establish our own error handler to free the row buffer. + * Some care must be taken to save/restore the caller's handler. + */ + memcpy(&parent, &png_jmpbuf(png), sizeof parent); + if (setjmp(png_jmpbuf(png))) { + png_free(png, row); + + memcpy(&png_jmpbuf(png), &parent, sizeof parent); + png_longjmp(png, 1); + } + + png_write_info(png, info); + for (png_uint_32 y = 0; y < height; y++) { + for (png_uint_32 x = 0; x < width; x++) { + row[x][0] = framedata[y][x]; + row[x][1] = mask[y][x] ? -1 : 0; + } + png_write_row(png, (void *)row); + } + png_write_end(png, NULL); + + memcpy(&png_jmpbuf(png), &parent, sizeof parent); +} + +static void +write_palette_frame(png_structp png, png_infop info, + unsigned char **framedata, struct lbx_colour *palette) { png_color png_palette[256]; @@ -153,11 +193,19 @@ write_index_frame(png_structp png, png_infop info, unsigned char **framedata, png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); } +static void +write_index_frame(png_structp png, png_infop info, unsigned char **framedata) +{ + png_set_rows(png, info, framedata); + png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL); +} + int img_output_png(FILE *f, const char *filename, unsigned width, unsigned height, unsigned char **framedata, unsigned char **mask, struct lbx_colour *palette) { + bool masked = is_masked(mask, width, height); struct file_info file = { filename, f }; png_structp png; png_infop info; @@ -183,7 +231,7 @@ int img_output_png(FILE *f, const char *filename, png_set_error_fn(png, NULL, error_fn, warning_fn); png_set_write_fn(png, &file, write_data, flush_data); - if (is_masked(mask, width, height)) { + if (palette && masked) { /* * Indexed colour mode can only be used for images without * transparency, since indexed PNG requires sacrificing one or @@ -196,13 +244,27 @@ int img_output_png(FILE *f, const char *filename, PNG_FILTER_TYPE_DEFAULT); write_rgba_frame(png, info, framedata, mask, palette); - } else { + } else if (palette) { png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - write_index_frame(png, info, framedata, palette); + write_palette_frame(png, info, framedata, palette); + } else if (masked) { + png_set_IHDR(png, info, width, height, 8, + PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + write_masked_index_frame(png, info, framedata, mask); + } else { + png_set_IHDR(png, info, width, height, 8, + PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + write_index_frame(png, info, framedata); } png_destroy_write_struct(&png, &info);