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_get_IHDR(png_ptr
, info_ptr
, &width
, &height
, &bit_depth
, &color_type
,
118 if ( color_type
== PNG_COLOR_TYPE_GRAY
) {
119 // Black & White or 8-bit grayscale
120 if ( bit_depth
== 1 && info_ptr
->channels
== 1 ) {
121 png_set_invert_mono( png_ptr
);
122 png_read_update_info( png_ptr
, info_ptr
);
123 if (!image
.create( width
, height
, 1, 2, QImage::BigEndian
))
125 image
.setColor( 1, qRgb(0,0,0) );
126 image
.setColor( 0, qRgb(255,255,255) );
127 } else if (bit_depth
== 16 && png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
)) {
128 png_set_expand(png_ptr
);
129 png_set_strip_16(png_ptr
);
130 png_set_gray_to_rgb(png_ptr
);
132 if (!image
.create(width
, height
, 32))
134 image
.setAlphaBuffer(true);
136 if (QImage::systemByteOrder() == QImage::BigEndian
)
137 png_set_swap_alpha(png_ptr
);
139 png_read_update_info(png_ptr
, info_ptr
);
141 if ( bit_depth
== 16 )
142 png_set_strip_16(png_ptr
);
143 else if ( bit_depth
< 8 )
144 png_set_packing(png_ptr
);
145 int ncols
= bit_depth
< 8 ? 1 << bit_depth
: 256;
146 png_read_update_info(png_ptr
, info_ptr
);
147 if (!image
.create(width
, height
, 8, ncols
))
149 for (int i
=0; i
<ncols
; i
++) {
150 int c
= i
*255/(ncols
-1);
151 image
.setColor( i
, qRgba(c
,c
,c
,0xff) );
153 if ( png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
) ) {
154 const int g
= info_ptr
->trans_values
.gray
;
156 image
.setAlphaBuffer(true);
157 image
.setColor(g
, image
.color(g
) & RGB_MASK
);
161 } else if ( color_type
== PNG_COLOR_TYPE_PALETTE
162 && png_get_valid(png_ptr
, info_ptr
, PNG_INFO_PLTE
)
163 && info_ptr
->num_palette
<= 256 )
165 // 1-bit and 8-bit color
166 if ( bit_depth
!= 1 )
167 png_set_packing( png_ptr
);
168 png_read_update_info( png_ptr
, info_ptr
);
169 png_get_IHDR(png_ptr
, info_ptr
,
170 &width
, &height
, &bit_depth
, &color_type
, 0, 0, 0);
171 if (!image
.create(width
, height
, bit_depth
, info_ptr
->num_palette
,
175 if ( png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
) ) {
176 image
.setAlphaBuffer( true );
177 while ( i
< info_ptr
->num_trans
) {
178 image
.setColor(i
, qRgba(
179 info_ptr
->palette
[i
].red
,
180 info_ptr
->palette
[i
].green
,
181 info_ptr
->palette
[i
].blue
,
188 while ( i
< info_ptr
->num_palette
) {
189 image
.setColor(i
, qRgba(
190 info_ptr
->palette
[i
].red
,
191 info_ptr
->palette
[i
].green
,
192 info_ptr
->palette
[i
].blue
,
200 if ( bit_depth
== 16 )
201 png_set_strip_16(png_ptr
);
203 png_set_expand(png_ptr
);
205 if ( color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
)
206 png_set_gray_to_rgb(png_ptr
);
208 if (!image
.create(width
, height
, 32))
211 // Only add filler if no alpha, or we can get 5 channel data.
212 if (!(color_type
& PNG_COLOR_MASK_ALPHA
)
213 && !png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
)) {
214 png_set_filler(png_ptr
, 0xff,
215 QImage::systemByteOrder() == QImage::BigEndian
?
216 PNG_FILLER_BEFORE
: PNG_FILLER_AFTER
);
217 // We want 4 bytes, but it isn't an alpha channel
219 image
.setAlphaBuffer(true);
222 if ( QImage::systemByteOrder() == QImage::BigEndian
) {
223 png_set_swap_alpha(png_ptr
);
226 png_read_update_info(png_ptr
, info_ptr
);
229 // Qt==ARGB==Big(ARGB)==Little(BGRA)
230 if ( QImage::systemByteOrder() == QImage::LittleEndian
) {
231 png_set_bgr(png_ptr
);
236 #if defined(Q_C_CALLBACKS)
239 static void CALLBACK_CALL_TYPE
qt_png_warning(png_structp
/*png_ptr*/, png_const_charp message
)
241 qWarning("libpng warning: %s", message
);
244 #if defined(Q_C_CALLBACKS)
249 QImage
splash_read_png_image(FILE* f
)
254 png_bytep
* row_pointers
;
256 png_ptr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
,0,0,0);
261 png_set_error_fn(png_ptr
, 0, 0, qt_png_warning
);
263 info_ptr
= png_create_info_struct(png_ptr
);
265 png_destroy_read_struct(&png_ptr
, 0, 0);
269 end_info
= png_create_info_struct(png_ptr
);
271 png_destroy_read_struct(&png_ptr
, &info_ptr
, 0);
275 if (setjmp(png_ptr
->jmpbuf
)) {
276 png_destroy_read_struct(&png_ptr
, &info_ptr
, &end_info
);
280 png_set_read_fn(png_ptr
, (void*)f
, iod_read_fn
);
281 png_read_info(png_ptr
, info_ptr
);
284 setup_qt(image
, png_ptr
, info_ptr
, 0);
285 if (image
.isNull()) {
286 png_destroy_read_struct(&png_ptr
, &info_ptr
, &end_info
);
294 png_get_IHDR(png_ptr
, info_ptr
, &width
, &height
, &bit_depth
, &color_type
,
297 uchar
** jt
= image
.jumpTable();
298 row_pointers
=new png_bytep
[height
];
300 for (uint y
=0; y
<height
; y
++) {
301 row_pointers
[y
]=jt
[y
];
304 png_read_image(png_ptr
, row_pointers
);
306 #if 0 // libpng takes care of this.
307 png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
)
308 if (image
.depth()==32 && png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
)) {
309 QRgb trans
= 0xFF000000 | qRgb(
310 (info_ptr
->trans_values
.red
<< 8 >> bit_depth
)&0xff,
311 (info_ptr
->trans_values
.green
<< 8 >> bit_depth
)&0xff,
312 (info_ptr
->trans_values
.blue
<< 8 >> bit_depth
)&0xff);
313 for (uint y
=0; y
<height
; y
++) {
314 for (uint x
=0; x
<info_ptr
->width
; x
++) {
315 if (((uint
**)jt
)[y
][x
] == trans
) {
316 ((uint
**)jt
)[y
][x
] &= 0x00FFFFFF;
324 image
.setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr
,info_ptr
));
325 image
.setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr
,info_ptr
));
327 #ifndef QT_NO_IMAGE_TEXT
330 png_get_text(png_ptr
,info_ptr
,&text_ptr
,&num_text
);
332 image
.setText(text_ptr
->key
,0,text_ptr
->text
);
337 delete [] row_pointers
;
339 if ( image
.hasAlphaBuffer() ) {
340 // Many PNG files lie (eg. from PhotoShop). Fortunately this loop will
341 // usually be quick to find those that tell the truth.
344 if (image
.depth()==32) {
345 c
= (QRgb
*)image
.bits();
346 n
= image
.bytesPerLine() * image
.height() / 4;
348 c
= image
.colorTable();
349 n
= image
.numColors();
351 while ( n
-- && qAlpha(*c
++)==0xff )
354 image
.setAlphaBuffer(false);
357 png_read_end(png_ptr
, end_info
);
358 png_destroy_read_struct(&png_ptr
, &info_ptr
, &end_info
);
362 #endif // QT_NO_IMAGEIO_PNG