/* render/pngfile.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ /* Notes: http://www.zarb.org/~gc/html/libpng.html */ #include "png.h" #include #include #include "sys/sys.h" #include "filesystem/filesystem.h" #include "render/pngfile.h" namespace render { Image *PNG::load(const char *filename) { Image *image = 0; if (!filename) return 0; filesystem::File *png_file = filesystem::open(filename); if (!png_file) { //con_warn << "Could not open " << filename << std::endl; return 0; } png_byte header[8]; memset(header, 0, sizeof(header)); if (!png_file->read(header, 8)) { con_warn << "Error reading " << filename << std::endl; filesystem::close(png_file); return 0; } if (png_sig_cmp(header, 0, 8)) { con_warn << "Error reading " << filename << ": not a PNG file!" << std::endl; filesystem::close(png_file); return 0; } png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { con_warn << "Error reading " << filename << ": png_create_read_struct failed!" << std::endl; filesystem::close(png_file); return 0; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { con_warn << "Error reading " << filename << ": png_create_info_struct failed!" << std::endl; filesystem::close(png_file); png_destroy_read_struct(&png_ptr, 0, 0); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error reading " << filename << ": error during init_io!" << std::endl; filesystem::close(png_file); png_destroy_read_struct(&png_ptr, &info_ptr, 0); return 0; } /* read the PNG header */ png_init_io(png_ptr, png_file->handle()); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); int png_width = png_get_image_width(png_ptr, info_ptr); // image width int png_height = png_get_image_height(png_ptr, info_ptr); // image height int png_depth = png_get_bit_depth(png_ptr, info_ptr); // bits per channel int png_channels = png_get_channels(png_ptr, info_ptr); // channels int png_rowbytes = png_get_rowbytes(png_ptr, info_ptr); // bytes per row png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); if (png_depth != 8) { con_warn << "Error reading " << filename << ": bits per channel must be 8!" << std::endl; filesystem::close(png_file); png_destroy_read_struct(&png_ptr, &info_ptr, 0); return 0; } image = new Image(png_width, png_height, png_channels); /* read image data */ if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error reading " << filename << std::endl; filesystem::close(png_file); delete image; png_destroy_read_struct(&png_ptr, &info_ptr, 0); return 0; } png_bytep row_pointers[png_height]; for (size_t i = 0; i < (size_t)png_height; i++) row_pointers[i] = (png_bytep)(*image)[i * png_rowbytes]; // read pixel data png_read_image(png_ptr, row_pointers); filesystem::close(png_file); png_destroy_read_struct(&png_ptr, &info_ptr, 0); con_debug << " " << filename << " " << image->width() << "x" << image->height() << "x" << image->bpp() << "bpp" << std::endl; return image; } void PNG::save(const char *filename, Image & image) { FILE *png_file = fopen(filename, "wb"); if (!png_file) { con_warn << "Could not write " << filename << std::endl; return; } png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fclose(png_file); con_warn << "Error writing " << filename << ": png_create_write_struct failed!" << std::endl; return; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { con_warn << "Error writing " << filename << ": png_create_info_struct failed!" << std::endl; fclose(png_file); png_destroy_write_struct(&png_ptr, 0); return; } if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error reading " << filename << ": error during init_io!" << std::endl; fclose(png_file); png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_init_io(png_ptr, png_file); /* write header */ if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error writing " << filename << ": error writing header!" << std::endl; fclose(png_file); png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); /* write image data */ if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error writing " << filename << ": error writing header!" << std::endl; fclose(png_file); png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_bytep row_pointers[image.height()]; for (size_t i = 0; i < image.height(); i++) row_pointers[i] = (png_bytep) image[i * image.width() * image.channels()]; png_write_image(png_ptr, row_pointers); /* end write */ if (setjmp(png_jmpbuf(png_ptr))) { con_warn << "Error writing " << filename << std::endl; fclose(png_file); png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_write_end(png_ptr, NULL); fclose(png_file); png_destroy_write_struct(&png_ptr, &info_ptr); } }