corona  1.0.2
OpenPNG.cpp
Go to the documentation of this file.
00001 
00008 #include <png.h>
00009 #include "Debug.h"
00010 #include "Open.h"
00011 #include "SimpleImage.h"
00012 #include "Utility.h"
00013 
00014 
00015 namespace corona {
00016 
00017   typedef unsigned char byte;
00018 
00019 
00021 
00022   void PNG_read_function(png_structp png_ptr,
00023                          png_bytep data,
00024                          png_size_t length) {
00025     File* file = (File*)png_get_io_ptr(png_ptr);
00026     if (file->read(data, length) != int(length)) {
00027       png_error(png_ptr, "Read error");
00028     }
00029   }
00030 
00032 
00033   void PNG_warning_function(png_structp png_ptr, png_const_charp error) {
00034     // no warnings
00035   }
00036 
00038 
00039   void PNG_error_function(png_structp png_ptr, png_const_charp warning) {
00040     // copied from libpng's pngerror.cpp, but without the fprintf
00041     //jmp_buf jmpbuf;
00042     //memcpy(jmpbuf, png_ptr->jmpbuf, sizeof(jmp_buf));
00043     //longjmp(jmpbuf, 1);
00044 
00045     // copied from libpng-1.5.12's pngerror.c. don't do anything? really?
00046     //png_longjmp(png_ptr, 1);
00047   }
00048 
00050 
00051   void fill_palette(png_structp png, png_infop info, png_color palette[256]) {
00052 
00053    COR_GUARD("fill_palette");
00054 
00055     // by default, the palette is greyscale
00056     for (int i = 0; i < 256; ++i) {
00057       palette[i].red   = i;
00058       palette[i].green = i;
00059       palette[i].blue  = i;
00060     }
00061 
00062     // do we have a palette and is it big enough?
00063     png_colorp png_palette;
00064     int num_palette = 0;
00065     png_get_PLTE(png, info, &png_palette, &num_palette);
00066 
00067     COR_IF_DEBUG {
00068       char str[80];
00069       sprintf(str, "palette size: %d", num_palette);
00070       COR_LOG(str);
00071     }
00072 
00073     if (num_palette >= 256) {
00074 
00075 #if 0
00076       COR_IF_DEBUG {
00077         for (int i = 0; i < 256; ++i) {
00078           char str[80];
00079           sprintf(str, "r(%d) g(%d) b(%d)",
00080             int(palette[i].red),
00081             int(palette[i].green),
00082             int(palette[i].blue));
00083           COR_LOG(str);
00084         }
00085       }
00086 #endif
00087 
00088       memcpy(palette, png_palette, 256 * sizeof(png_color));
00089     }
00090   }
00091 
00093 
00094   Image* OpenPNG(File* file) {
00095 
00096     COR_GUARD("OpenPNG");
00097 
00098     // verify PNG signature
00099     byte sig[8];
00100     file->read(sig, 8);
00101     if (png_sig_cmp(sig, 0, 8)) {
00102       return 0;
00103     }
00104 
00105     COR_LOG("Signature verified");
00106 
00107     // read struct
00108     png_structp png_ptr = png_create_read_struct(
00109       PNG_LIBPNG_VER_STRING,
00110       NULL, NULL, NULL);
00111     if (!png_ptr) {
00112       return 0;
00113     }
00114 
00115     COR_LOG("png struct created");
00116 
00117     // info struct
00118     png_infop info_ptr = png_create_info_struct(png_ptr);
00119     if (!info_ptr) {
00120       png_destroy_read_struct(&png_ptr, NULL, NULL);
00121       return 0;
00122     }
00123 
00124     COR_LOG("info struct created");
00125 
00126     // the PNG error function calls longjmp(png_ptr->jmpbuf)
00127     if (setjmp(png_jmpbuf(png_ptr))) {
00128       COR_LOG("Error loading PNG");
00129       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00130       return 0;
00131     }
00132 
00133     COR_LOG("setjmp() succeeded");
00134 
00135     // set the error function
00136     png_set_error_fn(png_ptr, 0, PNG_error_function, PNG_warning_function);
00137 
00138     // read the image
00139     png_set_read_fn(png_ptr, file, PNG_read_function);
00140     png_set_sig_bytes(png_ptr, 8);  // we already read 8 bytes for the sig
00141     // always give us 8-bit samples (strip 16-bit and expand <8-bit)
00142     int png_transform = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND;
00143     png_read_png(png_ptr, info_ptr, png_transform, NULL);
00144 
00145     COR_LOG("PNG read");
00146 
00147     if (!png_get_rows(png_ptr, info_ptr)) {
00148       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00149       return 0;
00150     }
00151 
00152     int width  = png_get_image_width(png_ptr, info_ptr);
00153     int height = png_get_image_height(png_ptr, info_ptr);
00154     byte* pixels = 0;  // allocate when we know the format
00155     PixelFormat format;
00156     byte* palette = 0;
00157     PixelFormat palette_format;
00158 
00159     // decode based on pixel format
00160     int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00161     int num_channels = png_get_channels(png_ptr, info_ptr);
00162     png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr);
00163 
00164     // 32-bit RGBA
00165     if (bit_depth == 8 && num_channels == 4) {
00166       COR_LOG("32-bit RGBA: bit_depth = 8 && num_channels = 4");
00167 
00168       format = PF_R8G8B8A8;
00169       pixels = new byte[width * height * 4];
00170       for (int i = 0; i < height; ++i) {
00171         memcpy(pixels + i * width * 4, row_pointers[i], width * 4);
00172       }
00173 
00174     // 24-bit RGB
00175     } else if (bit_depth == 8 && num_channels == 3) {
00176       COR_LOG("24-bit RGB: bit_depth = 8 && num_channels = 3");
00177 
00178       format = PF_R8G8B8;
00179       pixels = new byte[width * height * 3];
00180       for (int i = 0; i < height; ++i) {
00181         memcpy(pixels + i * width * 3, row_pointers[i], width * 3);
00182       }
00183 
00184     // palettized or greyscale with alpha
00185     } else if (bit_depth == 8 && (num_channels == 2 || num_channels == 1)) {
00186       png_color png_palette[256];
00187       fill_palette(png_ptr, info_ptr, png_palette);
00188 
00189       if (num_channels == 2) {
00190         COR_LOG("bit_depth = 8 && num_channels = 2");
00191 
00192         format = PF_R8G8B8A8;
00193         pixels = new byte[width * height * 4];
00194         byte* out = pixels;
00195 
00196         for (int i = 0; i < height; ++i) {
00197           byte* in = row_pointers[i];
00198           for (int j = 0; j < width; ++j) {
00199             byte c = *in++;
00200             *out++ = png_palette[c].red;
00201             *out++ = png_palette[c].green;
00202             *out++ = png_palette[c].blue;
00203             *out++ = *in++;  // alpha
00204           }
00205         }
00206 
00207       } else { // (num_channels == 1)
00208         COR_LOG("bit_depth = 8 && num_channels = 1");
00209 
00210         pixels = new byte[width * height];
00211         format = PF_I8;
00212         palette = new byte[256 * 4];
00213         palette_format = PF_R8G8B8A8;
00214 
00215 
00216         // get the transparent palette flags
00217         png_bytep trans;
00218         int num_trans = 0;
00219         png_color_16p trans_values; // XXX not used - should be?
00220         png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
00221 
00222         // copy the palette from the PNG
00223         for (int i = 0; i < 256; ++i) {
00224           palette[i * 4 + 0] = png_palette[i].red;
00225           palette[i * 4 + 1] = png_palette[i].green;
00226           palette[i * 4 + 2] = png_palette[i].blue;
00227           palette[i * 4 + 3] = 255;
00228         }
00229         // apply transparency to palette entries
00230         for (int i = 0; i < num_trans; ++i) {
00231           palette[trans[i] * 4 + 3] = 0;
00232         }
00233 
00234         byte* out = pixels;
00235         for (int i = 0; i < height; ++i) {
00236           memcpy(out, row_pointers[i], width);
00237           out += width;
00238         }
00239       }
00240 
00241     } else {  // unknown format
00242       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00243       return 0;
00244     }
00245 
00246     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00247 
00248     if (palette) {
00249       return new SimpleImage(width, height, format, pixels,
00250                              palette, 256, palette_format);
00251     } else {
00252       return new SimpleImage(width, height, format, pixels);
00253     }
00254   }
00255 
00257 
00258 }