corona
1.0.2
|
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 }