// // PImageLoaderPng.cc for pekwm // Copyright © 2005-2009 Claes Nästén // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #ifdef HAVE_IMAGE_PNG #include "PImageLoaderPng.hh" #include extern "C" { #include } using std::string; using std::cerr; using std::endl; const int PImageLoaderPng::PNG_SIG_BYTES = 8; //! @brief PImageLoaderPng constructor. PImageLoaderPng::PImageLoaderPng(void) : PImageLoader("PNG") { } //! @brief PImageLoaderPng destructor. PImageLoaderPng::~PImageLoaderPng(void) { } //! @brief Loads file into data. //! @param file File to load data from. //! @param width Set to the width of image. //! @param height Set to the height of image. //! @param alpha Set to wheter image has alpha channel. //! @return Pointer to data on success, else 0. uchar* PImageLoaderPng::load(const std::string &file, uint &width, uint &height, bool &alpha, bool &use_alpha) { FILE *fp; fp = fopen(file.c_str(), "rb"); if (! fp) { cerr << " *** WARNING: unable to open " << file << " for reading!" << endl; return 0; } if (! checkSignature(fp)) { cerr << " *** WARNING: " << file << " not a PNG file!" << endl; fclose(fp); return 0; } // Start PNG loading. png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (! png_ptr) { cerr << " *** ERROR: out of memory, png_create_read_struct failed!" << endl; fclose(fp); return 0; } info_ptr = png_create_info_struct(png_ptr); if (! info_ptr) { cerr << " *** ERROR: out of memory, png_create_info_struct failed!" << endl; png_destroy_read_struct(&png_ptr, 0, 0); fclose(fp); return 0; } // Setup error handling. if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); return 0; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, PImageLoaderPng::PNG_SIG_BYTES); png_read_info(png_ptr, info_ptr); int color_type, bpp; png_uint_32 png_width = 1, png_height = 1; png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bpp, &color_type, 0, 0, 0); width = png_width; height = png_height; // Setup read information, we want to make sure data get read in // 16 bit RGB(A) // palette -> RGB mode if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } // gray -> 8 bit gray if (color_type == PNG_COLOR_TYPE_GRAY && (bpp < 8)) { png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); } if (bpp == 16) { png_set_strip_16(png_ptr); } // gray, gray alpha -> to RGB if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(png_ptr); } // Now load image data. uchar *data; size_t data_size; png_uint_32 rowbytes, channels; png_bytepp row_pointers; png_read_update_info(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); channels = png_get_channels(png_ptr, info_ptr); data_size = rowbytes * height / sizeof(uchar); data = new uchar[data_size]; row_pointers = new png_bytep[height]; for (png_uint_32 y = 0; y < height; ++y) { row_pointers[y] = data + y * rowbytes; } png_read_image(png_ptr, row_pointers); delete [] row_pointers; png_read_end(png_ptr, 0); // Clean up. png_destroy_read_struct(&png_ptr, &info_ptr, 0); fclose(fp); alpha = (channels == 4); use_alpha = false; if (alpha) { uchar *p = data + 3, *end = data + data_size; for (; p != end && ! use_alpha; p += 4) { if (*p != 255) { use_alpha = true; } } } return data; } //! @brief Checks file signature to see if it's a PNG file. //! @param fp Pointer to open (rb) FILE. //! @return true if fp is a PNG file, else false. bool PImageLoaderPng::checkSignature(FILE *fp) { int status; uchar sig[PImageLoaderPng::PNG_SIG_BYTES]; status = fread(sig, 1, PImageLoaderPng::PNG_SIG_BYTES, fp); if (status == PImageLoaderPng::PNG_SIG_BYTES) { return (png_sig_cmp(sig,0,PImageLoaderPng::PNG_SIG_BYTES) == 0); } return false; } #endif // HAVE_IMAGE_PNG