diff options
Diffstat (limited to 'tools/depends/native/TexturePacker/src/decoder/PNGDecoder.cpp')
-rw-r--r-- | tools/depends/native/TexturePacker/src/decoder/PNGDecoder.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/tools/depends/native/TexturePacker/src/decoder/PNGDecoder.cpp b/tools/depends/native/TexturePacker/src/decoder/PNGDecoder.cpp new file mode 100644 index 0000000..f327400 --- /dev/null +++ b/tools/depends/native/TexturePacker/src/decoder/PNGDecoder.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2014 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "PNGDecoder.h" + +#include "SimpleFS.h" + +#include <png.h> + +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() + * returns zero if the image is a PNG and nonzero if it isn't a PNG. + * + * The function check_if_png() shown here, but not used, returns nonzero (true) + * if the file can be opened and is a PNG, 0 (false) otherwise. + * + * If this call is successful, and you are going to keep the file open, + * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once + * you have created the png_ptr, so that libpng knows your application + * has read that many bytes from the start of the file. Make sure you + * don't call png_set_sig_bytes() with more than 8 bytes read or give it + * an incorrect number of bytes read, or you will either have read too + * many bytes (your fault), or you are telling libpng to read the wrong + * number of magic bytes (also your fault). + * + * Many applications already read the first 2 or 4 bytes from the start + * of the image to determine the file type, so it would be easiest just + * to pass the bytes to png_sig_cmp() or even skip that if you know + * you have a PNG file, and call png_set_sig_bytes(). + */ +bool PNGDecoder::CanDecode(const std::string &filename) +{ + #define PNG_BYTES_TO_CHECK 4 + CFile fp; + char buf[PNG_BYTES_TO_CHECK]; + + /* Open the prospective PNG file. */ + if (!fp.Open(filename)) + return false; + + /* Read in some of the signature bytes */ + if (fp.Read(buf, PNG_BYTES_TO_CHECK) != PNG_BYTES_TO_CHECK) + { + fprintf(stderr, "error reading header ...\n"); + return false; + } + fp.Close(); + + /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. + Return nonzero (true) if they match */ + return(!png_sig_cmp((png_bytep)buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); +} + +bool PNGDecoder::LoadFile(const std::string &filename, DecodedFrames &frames) +{ + png_byte header[8]; + + CFile fp; + if (!fp.Open(filename)) + { + perror(filename.c_str()); + return false; + } + + // read the header + fp.Read(header, 8); + + if (png_sig_cmp(header, 0, 8)) + { + fprintf(stderr, "error: %s is not a PNG.\n", filename.c_str()); + return false; + } + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fprintf(stderr, "error: png_create_read_struct returned 0.\n"); + return false; + } + + // create png info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + fprintf(stderr, "error: png_create_info_struct returned 0.\n"); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return false; + } + + // create png info struct + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + fprintf(stderr, "error: png_create_info_struct returned 0.\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + return false; + } + + // the code in this if statement gets called if libpng encounters an error + if (setjmp(png_jmpbuf(png_ptr))) { + fprintf(stderr, "error from libpng\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return false; + } + + // init png reading + png_init_io(png_ptr, fp.getFP()); + + // let libpng know you already read the first 8 bytes + png_set_sig_bytes(png_ptr, 8); + + // read all the info up to the image data + png_read_info(png_ptr, info_ptr); + + // variables to pass to get info + int bit_depth, color_type; + png_uint_32 temp_width, temp_height; + + // get info about png + png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type, + NULL, NULL, NULL); + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_set_tRNS_to_alpha(png_ptr); + } + + //set it to 32bit pixeldepth + png_color_8 sig_bit; + sig_bit.red = 32; + sig_bit.green = 32; + sig_bit.blue = 32; + // if the image has an alpha channel then + sig_bit.alpha = 32; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + + /* Add filler (or alpha) byte (before/after each RGB triplet) */ + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + + // convert indexed color to rgb + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ + //png_set_swap_alpha(png_ptr); + + //libsquish only eats 32bit RGBA, must convert grayscale into this format + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_gray_to_rgb(png_ptr); + } + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + + // Row size in bytes. + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // glTexImage2d requires rows to be 4-byte aligned + // rowbytes += 3 - ((rowbytes-1) % 4); + + // Allocate the image_data as a big block, to be given to opengl + png_byte * image_data; + image_data = (png_byte*)new png_byte[rowbytes * temp_height * sizeof(png_byte)+15]; + if (image_data == NULL) + { + fprintf(stderr, "error: could not allocate memory for PNG image data\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return false; + } + + // row_pointers is for pointing to image_data for reading the png with libpng + png_bytep * row_pointers = (png_bytep*) new png_bytep[temp_height * sizeof(png_bytep)]; + if (row_pointers == NULL) + { + fprintf(stderr, "error: could not allocate memory for PNG row pointers\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete [] image_data; + return false; + } + + // set the individual row_pointers to point at the correct offsets of image_data + for (unsigned int i = 0; i < temp_height; i++) + { + row_pointers[i] = image_data + i * rowbytes; + } + + // read the png into image_data through row_pointers + png_read_image(png_ptr, row_pointers); + + DecodedFrame frame; + + frame.rgbaImage.pixels = (char *)image_data; + frame.rgbaImage.height = temp_height; + frame.rgbaImage.width = temp_width; + frame.rgbaImage.bbp = 32; + frame.rgbaImage.pitch = 4 * temp_width; + + frame.decoder = this; + + frames.frameList.push_back(frame); + // clean up + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete [] row_pointers; + return true; +} + +void PNGDecoder::FreeDecodedFrame(DecodedFrame &frame) +{ + delete [] frame.rgbaImage.pixels; +} + +void PNGDecoder::FillSupportedExtensions() +{ + m_supportedExtensions.emplace_back(".png"); +} |