]> git.draconx.ca Git - liblbx.git/commitdiff
lbximg: Push no-palette mode down into the PNG writer.
authorNick Bowler <nbowler@draconx.ca>
Fri, 14 Jun 2013 01:16:36 +0000 (21:16 -0400)
committerNick Bowler <nbowler@draconx.ca>
Fri, 14 Jun 2013 20:35:09 +0000 (16:35 -0400)
This allows the writer to select the most appropriate output format to
support this mode.  Switch the PNG writer to emit true grayscale images
in this case.

src/lbximg.c
src/png.c

index c761c176f6ee308e421cbdf67dfe73bdaaee0b2d..a7c60c6d54d0330a7111ba06cc9008891c7f739a 100644 (file)
@@ -172,15 +172,6 @@ static int loadpalette(struct lbx_image *img, struct lbx_imginfo *info,
 {
        int i;
 
-       /* In no-palette mode, use palette indices for colour. */
-       if (!usepalette) {
-               for (i = 0; i < 256; i++) {
-                       palette[i] = (struct lbx_colour){i,i,i};
-               }
-
-               return 0;
-       }
-
        /* For sanity. */
        if (!palf && !info->palettesz && !override) {
                tool_err(-1, "no palette available.");
@@ -237,8 +228,10 @@ int decode(struct lbx_image *img, FILE *palf, FILE *override, char **argv)
                }
        }
 
-       if (loadpalette(img, &info, palf, override, palette) == -1) {
-               goto err;
+       if (usepalette) {
+               if (loadpalette(img, &info, palf, override, palette) == -1) {
+                       goto err;
+               }
        }
 
        /* Extract the images, in order. */
@@ -257,7 +250,8 @@ int decode(struct lbx_image *img, FILE *palf, FILE *override, char **argv)
 
                mask = lbx_img_getmask(img);
 
-               if (!outpng(i, data, mask, img->width, img->height, palette)) {
+               if (!outpng(i, data, mask, img->width, img->height,
+                           usepalette ? palette : NULL)) {
                        extracted = 1;
                }
        }
index ee5d0a52644d5e12e3285817312d7dc2da79f228..288cf7719d227f83dc5c5f8342370149fc6ac9b2 100644 (file)
--- a/src/png.c
+++ b/src/png.c
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <setjmp.h>
 
 #include <png.h>
@@ -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);