Canola
0.8.D001
|
00001 // 00002 // canola - canon canola 1614p emulator 00003 // Copyright (C) 2011 Peter Miller 00004 // 00005 // This program is free software; you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License, version 3, as 00007 // published by the Free Software Foundation. 00008 // 00009 // This program is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License along 00015 // with this program. If not, see <http://www.gnu.org/licenses/>. 00016 // 00017 00018 #include <lib/config.h> 00019 #include <libexplain/fclose.h> 00020 #include <libexplain/fopen.h> 00021 #include <libexplain/fread.h> 00022 #include <libexplain/fwrite.h> 00023 #include <libexplain/output.h> 00024 #include <png.h> 00025 00026 #include <lib/image/format/png.h> 00027 00028 00029 image_format_png::~image_format_png() 00030 { 00031 } 00032 00033 00034 image_format_png::image_format_png(int a_width, int a_height) : 00035 image_format(a_width, a_height) 00036 { 00037 } 00038 00039 00040 image_format_png::image_format_png(int a_width, int a_height, data_t *a_data) : 00041 image_format(a_width, a_height, a_data) 00042 { 00043 } 00044 00045 00046 const char * 00047 image_format_png::type_name(void) 00048 const 00049 { 00050 return "PNG"; 00051 } 00052 00053 00054 static void 00055 input_read_glue(png_structp png_ptr, png_bytep data, png_size_t size) 00056 { 00057 FILE *fp = (FILE *)png_get_io_ptr(png_ptr); 00058 size_t check = explain_fread_or_die(data, 1, size, fp); 00059 if (check != size) 00060 png_error(png_ptr, "Read Error"); 00061 } 00062 00063 00064 image::pointer 00065 image_format_png::create(const std::string &filename) 00066 { 00067 // 00068 // open file and test for it being a png 00069 // 00070 FILE *fp = explain_fopen_or_die(filename.c_str(), "rb"); 00071 png_byte header[4]; 00072 explain_fread_or_die(header, sizeof(header), 1, fp); 00073 if (png_sig_cmp(header, 0, sizeof(header))) 00074 explain_output_error_and_die("%s: not a PNG file", filename.c_str()); 00075 00076 // 00077 // read the image from the file 00078 // 00079 png_structp png_ptr = 00080 png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 00081 if (!png_ptr) 00082 { 00083 oops: 00084 explain_output_error_and_die("read %s: oops", filename.c_str()); 00085 } 00086 00087 // If you want to understand what is happening here, read the PNG 00088 // book "PNG The Definitive Guide" by Greg Roelofs. 00089 // 00090 // The relevant chapter is available online as 00091 // http://www.libpng.org/pub/png/book/chapter13.html 00092 // 00093 png_info *info_ptr = png_create_info_struct(png_ptr); 00094 if (!info_ptr) 00095 goto oops; 00096 00097 if (setjmp(png_jmpbuf(png_ptr))) 00098 { 00099 explain_output_error_and_die("%s: error during init_io", 00100 filename.c_str()); 00101 } 00102 00103 png_set_read_fn(png_ptr, fp, input_read_glue); 00104 png_set_sig_bytes(png_ptr, sizeof(header)); 00105 png_read_info(png_ptr, info_ptr); 00106 00107 size_t width = png_get_image_width(png_ptr, info_ptr); 00108 assert(width >= 1); 00109 size_t height = png_get_image_height(png_ptr, info_ptr); 00110 assert(height >= 1); 00111 int color_type = png_get_color_type(png_ptr, info_ptr); 00112 int bit_depth = png_get_bit_depth(png_ptr, info_ptr); 00113 assert(bit_depth >= 1); 00114 00115 // force palette images to be expanded to 24-bit RGB. 00116 if (color_type == PNG_COLOR_TYPE_PALETTE) 00117 { 00118 png_set_palette_to_rgb(png_ptr); 00119 } 00120 00121 // low-bit-depth grayscale images are to be expanded to 8 bits 00122 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) 00123 { 00124 png_set_expand_gray_1_2_4_to_8(png_ptr); 00125 } 00126 00127 // expand any tRNS chunk data into a full alpha channel 00128 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 00129 { 00130 png_set_tRNS_to_alpha(png_ptr); 00131 } 00132 00133 // reduce images with 16-bit samples (e.g., 48-bit RGB) to 8 bits 00134 // per sample 00135 if (bit_depth == 16) 00136 png_set_strip_16(png_ptr); 00137 00138 // expand grayscale images to RGB 00139 if 00140 ( 00141 color_type == PNG_COLOR_TYPE_GRAY 00142 || 00143 color_type == PNG_COLOR_TYPE_GRAY_ALPHA 00144 ) 00145 png_set_gray_to_rgb(png_ptr); 00146 00147 // Once we've registered all of our desired transformations, we 00148 // request that libpng update the information struct appropriately 00149 png_read_update_info(png_ptr, info_ptr); 00150 00151 color_type = png_get_color_type(png_ptr, info_ptr); 00152 assert(color_type == PNG_COLOR_TYPE_RGB || 00153 color_type == PNG_COLOR_TYPE_RGB_ALPHA); 00154 bit_depth = png_get_bit_depth(png_ptr, info_ptr); 00155 assert(bit_depth == 8); 00156 00157 // read file 00158 if (setjmp(png_jmpbuf(png_ptr))) 00159 { 00160 explain_output_error_and_die("%s: error during read_image", 00161 filename.c_str()); 00162 } 00163 00164 size_t rgba_rowspan = width * 4; 00165 size_t png_rowbytes = png_get_rowbytes(png_ptr, info_ptr); 00166 size_t png_data_size = height * png_rowbytes; 00167 png_byte *png_data = new png_byte [png_data_size]; 00168 png_byte **row_pointers = new png_byte * [height]; 00169 for (size_t y = 0; y < height; ++y) 00170 row_pointers[y] = png_data + y * png_rowbytes; 00171 png_read_image(png_ptr, row_pointers); 00172 explain_fclose_or_die(fp); 00173 fp = 0; 00174 00175 // 00176 // Turn it into continuous RGBA data 00177 // 00178 if (png_rowbytes != rgba_rowspan) 00179 { 00180 assert(color_type == PNG_COLOR_TYPE_RGB); 00181 size_t rgba_size = height * rgba_rowspan; 00182 data_t *rgba_data = new data_t [rgba_size]; 00183 for (size_t y = 0; y < height; ++y) 00184 { 00185 data_t *rgba_row = rgba_data + y * rgba_rowspan; 00186 png_byte *png_row = row_pointers[y]; 00187 for (size_t x = 0; x < width; ++x) 00188 { 00189 *rgba_row++ = *png_row++; 00190 *rgba_row++ = *png_row++; 00191 *rgba_row++ = *png_row++; 00192 *rgba_row++ = 255; 00193 } 00194 } 00195 delete [] png_data; 00196 png_data = rgba_data; 00197 } 00198 delete [] row_pointers; 00199 00200 // 00201 // Finally, we have an image. 00202 // 00203 return pointer(new image_format_png(width, height, png_data)); 00204 } 00205 00206 00207 static void 00208 output_write_glue(png_structp png_ptr, png_bytep data, png_size_t size) 00209 { 00210 FILE *fp = (FILE *)png_get_io_ptr(png_ptr); 00211 explain_fwrite_or_die(data, 1, size, fp); 00212 } 00213 00214 00215 bool 00216 image_format_png::calc_needs_alpha(void) 00217 const 00218 { 00219 for (unsigned y = 0; y < get_height(); ++y) 00220 { 00221 png_byte *p = (png_byte *)get_data(y); 00222 for (unsigned x = 0; x < get_width(); ++x, p += 4) 00223 { 00224 if (p[3] != 255) 00225 return true; 00226 } 00227 } 00228 return false; 00229 } 00230 00231 00232 bool 00233 image_format_png::calc_needs_rgb(void) 00234 const 00235 { 00236 for (unsigned y = 0; y < get_height(); ++y) 00237 { 00238 png_byte *p = (png_byte *)get_data(y); 00239 for (unsigned x = 0; x < get_width(); ++x, p += 4) 00240 { 00241 if (p[3] != 0 && (p[0] != p[1] || p[0] != p[2])) 00242 return true; 00243 } 00244 } 00245 return false; 00246 } 00247 00248 00249 void 00250 image_format_png::save_to_file(const std::string &filename) 00251 const 00252 { 00253 // 00254 // create file 00255 // 00256 FILE *fp = explain_fopen_or_die(filename.c_str(), "wb"); 00257 00258 // initialize stuff 00259 png_structp png_ptr = 00260 png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 00261 if (!png_ptr) 00262 { 00263 oops: 00264 explain_output_error_and_die 00265 ( 00266 "%s: oops, write failed", 00267 filename.c_str() 00268 ); 00269 } 00270 png_info *info_ptr = png_create_info_struct(png_ptr); 00271 if (!info_ptr) 00272 goto oops; 00273 if (setjmp(png_jmpbuf(png_ptr))) 00274 { 00275 explain_output_error_and_die 00276 ( 00277 "%s: errror during init_io", 00278 filename.c_str() 00279 ); 00280 } 00281 png_set_write_fn(png_ptr, fp, output_write_glue, 0); 00282 00283 // write header 00284 if (setjmp(png_jmpbuf(png_ptr))) 00285 { 00286 explain_output_error_and_die 00287 ( 00288 "%s: error writing header", 00289 filename.c_str() 00290 ); 00291 } 00292 00293 bool needs_alpha = calc_needs_alpha(); 00294 bool needs_rgb = calc_needs_rgb(); 00295 int color_type = 00296 ( 00297 needs_rgb 00298 ? 00299 (needs_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB) 00300 : 00301 (needs_alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY) 00302 ); 00303 png_set_IHDR(png_ptr, info_ptr, get_width(), get_height(), 00304 8, color_type, PNG_INTERLACE_NONE, 00305 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 00306 png_write_info(png_ptr, info_ptr); 00307 00308 // write bytes 00309 if (setjmp(png_jmpbuf(png_ptr))) 00310 { 00311 explain_output_error_and_die 00312 ( 00313 "%s: error writing bytes", 00314 filename.c_str() 00315 ); 00316 } 00317 if (needs_rgb) 00318 { 00319 if (needs_alpha) 00320 { 00321 png_byte **row_pointers = new png_byte * [get_height()]; 00322 for (size_t y = 0; y < get_height(); ++y) 00323 row_pointers[y] = (png_byte *)get_data(y); 00324 png_write_image(png_ptr, row_pointers); 00325 delete [] row_pointers; 00326 } 00327 else 00328 { 00329 png_byte *ob_base = new png_byte [get_width() * 3]; 00330 for (unsigned y = 0; y < get_height(); ++y) 00331 { 00332 png_byte *ob = ob_base; 00333 png_byte *ib = (png_byte *)get_data(y); 00334 for (unsigned x = 0; x < get_width(); ++x, ib += 4) 00335 { 00336 *ob++ = ib[0]; 00337 *ob++ = ib[1]; 00338 *ob++ = ib[2]; 00339 } 00340 png_write_row(png_ptr, ob_base); 00341 } 00342 delete ob_base; 00343 } 00344 } 00345 else 00346 { 00347 if (needs_alpha) 00348 { 00349 png_byte *ob_base = new png_byte [get_width() * 2]; 00350 for (unsigned y = 0; y < get_height(); ++y) 00351 { 00352 png_byte *ob = ob_base; 00353 png_byte *ib = (png_byte *)get_data(y); 00354 for (unsigned x = 0; x < get_width(); ++x, ib += 4) 00355 { 00356 *ob++ = ib[0]; 00357 *ob++ = ib[3]; 00358 } 00359 png_write_row(png_ptr, ob_base); 00360 } 00361 delete ob_base; 00362 } 00363 else 00364 { 00365 png_byte *ob_base = new png_byte [get_width()]; 00366 for (unsigned y = 0; y < get_height(); ++y) 00367 { 00368 png_byte *ob = ob_base; 00369 png_byte *ib = (png_byte *)get_data(y); 00370 for (unsigned x = 0; x < get_width(); ++x, ib += 4) 00371 { 00372 *ob++ = ib[0]; 00373 } 00374 png_write_row(png_ptr, ob_base); 00375 } 00376 delete ob_base; 00377 } 00378 } 00379 00380 // end write 00381 if (setjmp(png_jmpbuf(png_ptr))) 00382 explain_output_error_and_die("%s: error finishing", filename.c_str()); 00383 png_write_end(png_ptr, NULL); 00384 00385 explain_fclose_or_die(fp); 00386 } 00387 00388 00389 image::pointer 00390 image_format_png::create_blank(int width, int height) 00391 { 00392 return pointer(new image_format_png(width, height)); 00393 }