/**************************************************************************** ** ** This file is based on sources of the Qt GUI Toolkit, used under the terms ** of the GNU General Public License version 2 (see the original copyright ** notice below). ** All further contributions to this file are (and are required to be) ** licensed under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** The original Qt license header follows: ** ** ** Implementation of PNG QImage IOHandler ** ** Created : 970521 ** ** Copyright (C) 1992-2003 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QT_NO_IMAGEIO_PNG #include "qimage.h" #include "qcolor.h" #include #ifdef Q_OS_TEMP #define CALLBACK_CALL_TYPE __cdecl #else #define CALLBACK_CALL_TYPE #endif /* All PNG files load to the minimal QImage equivalent. All QImage formats output to reasonably efficient PNG equivalents. Never to grayscale. */ #if defined(Q_C_CALLBACKS) extern "C" { #endif static void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { FILE* in = (FILE*)png_get_io_ptr(png_ptr); while (length) { int nr = fread( (char*)data, 1, length, in ); if (nr <= 0) { png_error(png_ptr, "Read Error"); return; } length -= nr; } } #if defined(Q_C_CALLBACKS) } #endif static void setup_qt( QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0 ) { if ( 0.0 == screen_gamma ) // PNG docs say this is a good guess for a PC monitor // in a dark room screen_gamma = 2.2; if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) ) { // the file has a gAMA attribute double file_gamma; if ( png_get_gAMA(png_ptr, info_ptr, &file_gamma)) png_set_gamma( png_ptr, screen_gamma, file_gamma ); } else { // no file gamma, use a reasonable default png_set_gamma( png_ptr, screen_gamma, 0.45455 ); } png_uint_32 width; png_uint_32 height; int bit_depth; int color_type; png_bytep trans_alpha = 0; png_color_16p trans_color_p = 0; int num_trans; png_colorp palette = 0; int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); if ( color_type == PNG_COLOR_TYPE_GRAY ) { // Black & White or 8-bit grayscale if ( bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1 ) { png_set_invert_mono( png_ptr ); png_read_update_info( png_ptr, info_ptr ); if (!image.create( width, height, 1, 2, QImage::BigEndian )) return; image.setColor( 1, qRgb(0,0,0) ); image.setColor( 0, qRgb(255,255,255) ); } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); png_set_strip_16(png_ptr); png_set_gray_to_rgb(png_ptr); if (!image.create(width, height, 32)) return; image.setAlphaBuffer(true); if (QImage::systemByteOrder() == QImage::BigEndian) png_set_swap_alpha(png_ptr); png_read_update_info(png_ptr, info_ptr); } else { if ( bit_depth == 16 ) png_set_strip_16(png_ptr); else if ( bit_depth < 8 ) png_set_packing(png_ptr); int ncols = bit_depth < 8 ? 1 << bit_depth : 256; png_read_update_info(png_ptr, info_ptr); if (!image.create(width, height, 8, ncols)) return; for (int i=0; igray; if (g < ncols) { image.setColor(g, 0); } } } } else if (color_type == PNG_COLOR_TYPE_PALETTE && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) && num_palette <= 256) { // 1-bit and 8-bit color if ( bit_depth != 1 ) png_set_packing( png_ptr ); png_read_update_info( png_ptr, info_ptr ); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); if (!image.create(width, height, bit_depth, num_palette, QImage::BigEndian)) return; int i = 0; png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) { image.setAlphaBuffer( true ); while ( i < num_trans ) { image.setColor(i, qRgba( palette[i].red, palette[i].green, palette[i].blue, trans_alpha[i] ) ); i++; } } while ( i < num_palette ) { image.setColor(i, qRgba( palette[i].red, palette[i].green, palette[i].blue, 0xff ) ); i++; } } else { // 32-bit if ( bit_depth == 16 ) png_set_strip_16(png_ptr); png_set_expand(png_ptr); if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) png_set_gray_to_rgb(png_ptr); if (!image.create(width, height, 32)) return; // Only add filler if no alpha, or we can get 5 channel data. if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_filler(png_ptr, 0xff, QImage::systemByteOrder() == QImage::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); // We want 4 bytes, but it isn't an alpha channel } else { image.setAlphaBuffer(true); } if ( QImage::systemByteOrder() == QImage::BigEndian ) { png_set_swap_alpha(png_ptr); } png_read_update_info(png_ptr, info_ptr); } // Qt==ARGB==Big(ARGB)==Little(BGRA) if ( QImage::systemByteOrder() == QImage::LittleEndian ) { png_set_bgr(png_ptr); } } #if defined(Q_C_CALLBACKS) extern "C" { #endif static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message) { qWarning("libpng warning: %s", message); } #if defined(Q_C_CALLBACKS) } #endif QImage splash_read_png_image(FILE* f) { png_structp png_ptr; png_infop info_ptr; png_infop end_info; png_bytep* row_pointers; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { return QImage(); } png_set_error_fn(png_ptr, 0, 0, qt_png_warning); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, 0, 0); return QImage(); } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); return QImage(); } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return QImage(); } png_set_read_fn(png_ptr, (void*)f, iod_read_fn); png_read_info(png_ptr, info_ptr); QImage image; setup_qt(image, png_ptr, info_ptr, 0); if (image.isNull()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return QImage(); } png_uint_32 width; png_uint_32 height; int bit_depth; int color_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); uchar** jt = image.jumpTable(); row_pointers=new png_bytep[height]; for (uint y=0; ytrans_values.red << 8 >> bit_depth)&0xff, (info_ptr->trans_values.green << 8 >> bit_depth)&0xff, (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff); for (uint y=0; ywidth; x++) { if (((uint**)jt)[y][x] == trans) { ((uint**)jt)[y][x] &= 0x00FFFFFF; } else { } } } } #endif image.setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr)); image.setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr)); #ifndef QT_NO_IMAGE_TEXT png_textp text_ptr; int num_text=0; png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); while (num_text--) { image.setText(text_ptr->key,0,text_ptr->text); text_ptr++; } #endif delete [] row_pointers; if ( image.hasAlphaBuffer() ) { // Many PNG files lie (eg. from PhotoShop). Fortunately this loop will // usually be quick to find those that tell the truth. QRgb* c; int n; if (image.depth()==32) { c = (QRgb*)image.bits(); n = image.bytesPerLine() * image.height() / 4; } else { c = image.colorTable(); n = image.numColors(); } while ( n-- && qAlpha(*c++)==0xff ) ; if ( n<0 ) // LIAR! image.setAlphaBuffer(false); } png_read_end(png_ptr, end_info); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return image; } #endif // QT_NO_IMAGEIO_PNG