1
/****************************************************************************
3
** This file is based on sources of the Qt GUI Toolkit, used under the terms
4
** of the GNU General Public License version 2 (see the original copyright
6
** All further contributions to this file are (and are required to be)
7
** licensed under the terms of the GNU General Public License as published by
8
** the Free Software Foundation; either version 2 of the License, or
9
** (at your option) any later version.
11
** The original Qt license header follows:
14
** Implementation of PNG QImage IOHandler
18
** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
20
** This file is part of the kernel module of the Qt GUI Toolkit.
22
** This file may be distributed under the terms of the Q Public License
23
** as defined by Trolltech AS of Norway and appearing in the file
24
** LICENSE.QPL included in the packaging of this file.
26
** This file may be distributed and/or modified under the terms of the
27
** GNU General Public License version 2 as published by the Free Software
28
** Foundation and appearing in the file LICENSE.GPL included in the
29
** packaging of this file.
31
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
32
** licenses may use this file in accordance with the Qt Commercial License
33
** Agreement provided with the Software.
35
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
36
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
38
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
39
** information about Qt Commercial License Agreements.
40
** See http://www.trolltech.com/qpl/ for QPL licensing information.
41
** See http://www.trolltech.com/gpl/ for GPL licensing information.
43
** Contact info@trolltech.com if any conditions of this licensing are
46
**********************************************************************/
48
#ifndef QT_NO_IMAGEIO_PNG
57
#define CALLBACK_CALL_TYPE __cdecl
59
#define CALLBACK_CALL_TYPE
64
All PNG files load to the minimal QImage equivalent.
66
All QImage formats output to reasonably efficient PNG equivalents.
70
#if defined(Q_C_CALLBACKS)
75
void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
77
FILE* in = (FILE*)png_get_io_ptr(png_ptr);
80
int nr = fread( (char*)data, 1, length, in );
82
png_error(png_ptr, "Read Error");
90
#if defined(Q_C_CALLBACKS)
95
void setup_qt( QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0 )
97
if ( 0.0 == screen_gamma )
98
// PNG docs say this is a good guess for a PC monitor
101
if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) ) {
102
// the file has a gAMA attribute
104
if ( png_get_gAMA(png_ptr, info_ptr, &file_gamma))
105
png_set_gamma( png_ptr, screen_gamma, file_gamma );
107
// no file gamma, use a reasonable default
108
png_set_gamma( png_ptr, screen_gamma, 0.45455 );
115
png_bytep trans_alpha = 0;
116
png_color_16p trans_color_p = 0;
118
png_colorp palette = 0;
120
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
122
if ( color_type == PNG_COLOR_TYPE_GRAY ) {
123
// Black & White or 8-bit grayscale
124
if ( bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1 ) {
125
png_set_invert_mono( png_ptr );
126
png_read_update_info( png_ptr, info_ptr );
127
if (!image.create( width, height, 1, 2, QImage::BigEndian ))
129
image.setColor( 1, qRgb(0,0,0) );
130
image.setColor( 0, qRgb(255,255,255) );
131
} else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
132
png_set_expand(png_ptr);
133
png_set_strip_16(png_ptr);
134
png_set_gray_to_rgb(png_ptr);
136
if (!image.create(width, height, 32))
138
image.setAlphaBuffer(true);
140
if (QImage::systemByteOrder() == QImage::BigEndian)
141
png_set_swap_alpha(png_ptr);
143
png_read_update_info(png_ptr, info_ptr);
145
if ( bit_depth == 16 )
146
png_set_strip_16(png_ptr);
147
else if ( bit_depth < 8 )
148
png_set_packing(png_ptr);
149
int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
150
png_read_update_info(png_ptr, info_ptr);
151
if (!image.create(width, height, 8, ncols))
153
for (int i=0; i<ncols; i++) {
154
int c = i*255/(ncols-1);
155
image.setColor( i, qRgba(c,c,c,0xff) );
157
if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
158
const int g = trans_color_p->gray;
160
image.setColor(g, 0);
164
} else if (color_type == PNG_COLOR_TYPE_PALETTE
165
&& png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
166
&& num_palette <= 256)
168
// 1-bit and 8-bit color
169
if ( bit_depth != 1 )
170
png_set_packing( png_ptr );
171
png_read_update_info( png_ptr, info_ptr );
172
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
173
if (!image.create(width, height, bit_depth, num_palette,
177
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
178
if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
179
image.setAlphaBuffer( true );
180
while ( i < num_trans ) {
181
image.setColor(i, qRgba(
191
while ( i < num_palette ) {
192
image.setColor(i, qRgba(
203
if ( bit_depth == 16 )
204
png_set_strip_16(png_ptr);
206
png_set_expand(png_ptr);
208
if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
209
png_set_gray_to_rgb(png_ptr);
211
if (!image.create(width, height, 32))
214
// Only add filler if no alpha, or we can get 5 channel data.
215
if (!(color_type & PNG_COLOR_MASK_ALPHA)
216
&& !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
217
png_set_filler(png_ptr, 0xff,
218
QImage::systemByteOrder() == QImage::BigEndian ?
219
PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
220
// We want 4 bytes, but it isn't an alpha channel
222
image.setAlphaBuffer(true);
225
if ( QImage::systemByteOrder() == QImage::BigEndian ) {
226
png_set_swap_alpha(png_ptr);
229
png_read_update_info(png_ptr, info_ptr);
232
// Qt==ARGB==Big(ARGB)==Little(BGRA)
233
if ( QImage::systemByteOrder() == QImage::LittleEndian ) {
234
png_set_bgr(png_ptr);
239
#if defined(Q_C_CALLBACKS)
242
static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
244
qWarning("libpng warning: %s", message);
247
#if defined(Q_C_CALLBACKS)
252
QImage splash_read_png_image(FILE* f)
257
png_bytep* row_pointers;
259
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
264
png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
266
info_ptr = png_create_info_struct(png_ptr);
268
png_destroy_read_struct(&png_ptr, 0, 0);
272
end_info = png_create_info_struct(png_ptr);
274
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
278
if (setjmp(png_jmpbuf(png_ptr))) {
279
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
283
png_set_read_fn(png_ptr, (void*)f, iod_read_fn);
284
png_read_info(png_ptr, info_ptr);
287
setup_qt(image, png_ptr, info_ptr, 0);
288
if (image.isNull()) {
289
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
297
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
300
uchar** jt = image.jumpTable();
301
row_pointers=new png_bytep[height];
303
for (uint y=0; y<height; y++) {
304
row_pointers[y]=jt[y];
307
png_read_image(png_ptr, row_pointers);
309
#if 0 // libpng takes care of this.
310
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
311
if (image.depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
312
QRgb trans = 0xFF000000 | qRgb(
313
(info_ptr->trans_values.red << 8 >> bit_depth)&0xff,
314
(info_ptr->trans_values.green << 8 >> bit_depth)&0xff,
315
(info_ptr->trans_values.blue << 8 >> bit_depth)&0xff);
316
for (uint y=0; y<height; y++) {
317
for (uint x=0; x<info_ptr->width; x++) {
318
if (((uint**)jt)[y][x] == trans) {
319
((uint**)jt)[y][x] &= 0x00FFFFFF;
327
image.setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
328
image.setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
330
#ifndef QT_NO_IMAGE_TEXT
333
png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
335
image.setText(text_ptr->key,0,text_ptr->text);
340
delete [] row_pointers;
342
if ( image.hasAlphaBuffer() ) {
343
// Many PNG files lie (eg. from PhotoShop). Fortunately this loop will
344
// usually be quick to find those that tell the truth.
347
if (image.depth()==32) {
348
c = (QRgb*)image.bits();
349
n = image.bytesPerLine() * image.height() / 4;
351
c = image.colorTable();
352
n = image.numColors();
354
while ( n-- && qAlpha(*c++)==0xff )
357
image.setAlphaBuffer(false);
360
png_read_end(png_ptr, end_info);
361
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
365
#endif // QT_NO_IMAGEIO_PNG