changeset 453:349a2ff11531

Implement PNG (1-8bpp indexed) reading support via libPNG.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 04 Nov 2012 12:07:59 +0200
parents d1f7ddc84c7c
children 2f820412d356
files libgfx.c
diffstat 1 files changed, 177 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/libgfx.c	Sun Nov 04 12:07:03 2012 +0200
+++ b/libgfx.c	Sun Nov 04 12:07:59 2012 +0200
@@ -464,6 +464,181 @@
     fclose(fp);
     return res;
 }
+
+
+int dmReadPNGImageFILE(FILE *fp, DMImage **pimg)
+{
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
+    png_colorp palette = NULL;
+    png_bytep *row_pointers = NULL;
+    png_uint_32 width, height;
+    int i, bit_depth, color_type, ncolors;
+    int res = DMERR_OK;
+    DMImage *img;
+
+    // Create PNG structures
+    png_ptr = png_create_read_struct(
+        PNG_LIBPNG_VER_STRING,
+        NULL, NULL, NULL);
+
+    if (png_ptr == NULL)
+    {
+        dmError("PNG: png_create_write_struct() failed.\n");
+        res = DMERR_MALLOC;
+        goto error;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+    if (info_ptr == NULL)
+    {
+        dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr);
+        res = DMERR_INIT_FAIL;
+        goto error;
+    }
+    
+    if (setjmp(png_jmpbuf(png_ptr)))
+    {
+        dmError("PNG: Error during image reading..\n");
+        res = DMERR_INIT_FAIL;
+        goto error;
+    }
+
+    png_init_io(png_ptr, fp);
+
+    // Read image information
+    png_read_info(png_ptr, info_ptr);
+
+    png_get_IHDR(png_ptr, info_ptr, &width, &height,
+        &bit_depth, &color_type, NULL, NULL, NULL);
+
+    if (width < 1 || height < 1)
+    {
+        dmError("PNG: Invalid width or height (%d x %d)\n",
+            width, height);
+        res = DMERR_INVALID_DATA;
+        goto error;
+    }
+
+    switch (color_type)
+    {
+        case PNG_COLOR_TYPE_GRAY:
+            if (bit_depth < 8)
+                png_set_expand_gray_1_2_4_to_8(png_ptr);
+
+            if (bit_depth > 8)
+            {
+                dmError("PNG: Unsupported bit depth for grayscale image: %d\n",
+                    bit_depth);
+                res = DMERR_NOT_SUPPORTED;
+                goto error;
+            }
+            break;
+        
+        case PNG_COLOR_TYPE_PALETTE:
+            png_set_packing(png_ptr);
+            break;
+
+        default:
+            dmError("PNG: RGB/RGBA images not supported for loading.\n");
+            res = DMERR_NOT_SUPPORTED;
+            goto error;
+    }
+
+    // Allocate image
+    dmMsg(2, "PNG: %d x %d, depth=%d, type=%d\n",
+        width, height, bit_depth, color_type);
+
+    if ((*pimg = img = dmImageAlloc(width, height)) == NULL)
+    {
+        dmError("PNG: Could not allocate image data.\n");
+        res = DMERR_MALLOC;
+        goto error;
+    }
+
+    // ...
+    row_pointers = png_malloc(png_ptr, height * sizeof(png_bytep));
+    for (i = 0; i < img->height; i++)
+        row_pointers[i] = img->data + (i * img->pitch);
+
+    png_read_image(png_ptr, row_pointers);
+
+    png_read_end(png_ptr, NULL);
+
+    // Create palette
+    palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
+    if (palette == NULL)
+    {
+        dmError("PNG: Could not allocate palette structure.");
+        res = DMERR_MALLOC;
+        goto error;
+    }
+    
+    switch (color_type)
+    {
+        case PNG_COLOR_TYPE_GRAY:
+            ncolors = 256;
+            dmMsg(2, "PNG: Generating %d color grayscale palette.\n", ncolors);
+
+            if (!dmImageAllocPalette(img, ncolors))
+            {
+                res = DMERR_MALLOC;
+                goto error;
+            }
+
+            for (i = 0; i < img->ncolors; i++)
+            {
+                img->pal[i].r = img->pal[i].g = img->pal[i].b = i;
+            }
+            break;
+        
+        case PNG_COLOR_TYPE_PALETTE:
+            png_get_PLTE(png_ptr, info_ptr, &palette, &ncolors);
+            dmMsg(2, "PNG: Palette of %d colors found.\n", ncolors);            
+            if (ncolors <= 0)
+                goto error;
+
+            if (!dmImageAllocPalette(img, ncolors))
+            {
+                res = DMERR_MALLOC;
+                goto error;
+            }
+
+            for (i = 0; i < img->ncolors; i++)
+            {
+                img->pal[i].r = palette[i].red;
+                img->pal[i].g = palette[i].green;
+                img->pal[i].b = palette[i].blue;
+            }
+            break;
+    }
+
+error:
+    png_free(png_ptr, palette);
+
+    if (png_ptr && info_ptr)
+        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+    return res;
+}
+
+
+int dmReadPNGImage(const char *filename, DMImage **img)
+{
+    int res;
+    FILE *fp;
+
+    if ((fp = fopen(filename, "rb")) == NULL)
+    {
+        dmError("PNG: Could not open file '%s' for reading.\n", filename);
+        return DMERR_FOPEN;
+    }
+
+    res = dmReadPNGImageFILE(fp, img);
+
+    fclose(fp);
+    return res;
+}
 #endif
 
 
@@ -1488,11 +1663,12 @@
     {
         "PNG", "Portable Network Graphics",
         fmtProbePNG,
-        NULL, NULL,
 #ifdef DM_USE_LIBPNG
+        dmReadPNGImage, dmReadPNGImageFILE,
         dmWritePNGImage, dmWritePNGImageFILE,
 #else
         NULL, NULL,
+        NULL, NULL,
 #endif
     },
     {