Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / ksplash / ksplashx / qpngio.cpp
blob8742a17f53e9d1187cc6b5d283d2a88f5a37080a
1 /****************************************************************************
2 **
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
5 ** notice below).
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:
12 **
14 ** Implementation of PNG QImage IOHandler
16 ** Created : 970521
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
44 ** not clear to you.
46 **********************************************************************/
48 #ifndef QT_NO_IMAGEIO_PNG
50 #include "qimage.h"
51 #include "qcolor.h"
53 #include <png.h>
56 #ifdef Q_OS_TEMP
57 #define CALLBACK_CALL_TYPE __cdecl
58 #else
59 #define CALLBACK_CALL_TYPE
60 #endif
64 All PNG files load to the minimal QImage equivalent.
66 All QImage formats output to reasonably efficient PNG equivalents.
67 Never to grayscale.
70 #if defined(Q_C_CALLBACKS)
71 extern "C" {
72 #endif
74 static
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);
79 while (length) {
80 int nr = fread( (char*)data, 1, length, in );
81 if (nr <= 0) {
82 png_error(png_ptr, "Read Error");
83 return;
85 length -= nr;
90 #if defined(Q_C_CALLBACKS)
92 #endif
94 static
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
99 // in a dark room
100 screen_gamma = 2.2;
101 if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) ) {
102 // the file has a gAMA attribute
103 double file_gamma;
104 if ( png_get_gAMA(png_ptr, info_ptr, &file_gamma))
105 png_set_gamma( png_ptr, screen_gamma, file_gamma );
106 } else {
107 // no file gamma, use a reasonable default
108 png_set_gamma( png_ptr, screen_gamma, 0.45455 );
111 png_uint_32 width;
112 png_uint_32 height;
113 int bit_depth;
114 int color_type;
115 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
116 0, 0, 0);
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 ))
124 return;
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))
133 return;
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);
140 } else {
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))
148 return;
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;
155 if (g < ncols) {
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,
172 QImage::BigEndian))
173 return;
174 int i = 0;
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,
182 info_ptr->trans[i]
185 i++;
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,
193 0xff
196 i++;
198 } else {
199 // 32-bit
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))
209 return;
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
218 } else {
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)
237 extern "C" {
238 #endif
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)
246 #endif
249 QImage splash_read_png_image(FILE* f)
251 png_structp png_ptr;
252 png_infop info_ptr;
253 png_infop end_info;
254 png_bytep* row_pointers;
256 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
257 if (!png_ptr) {
258 return QImage();
261 png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
263 info_ptr = png_create_info_struct(png_ptr);
264 if (!info_ptr) {
265 png_destroy_read_struct(&png_ptr, 0, 0);
266 return QImage();
269 end_info = png_create_info_struct(png_ptr);
270 if (!end_info) {
271 png_destroy_read_struct(&png_ptr, &info_ptr, 0);
272 return QImage();
275 if (setjmp(png_ptr->jmpbuf)) {
276 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
277 return QImage();
280 png_set_read_fn(png_ptr, (void*)f, iod_read_fn);
281 png_read_info(png_ptr, info_ptr);
283 QImage image;
284 setup_qt(image, png_ptr, info_ptr, 0);
285 if (image.isNull()) {
286 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
287 return QImage();
290 png_uint_32 width;
291 png_uint_32 height;
292 int bit_depth;
293 int color_type;
294 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
295 0, 0, 0);
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;
317 } else {
322 #endif
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
328 png_textp text_ptr;
329 int num_text=0;
330 png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
331 while (num_text--) {
332 image.setText(text_ptr->key,0,text_ptr->text);
333 text_ptr++;
335 #endif
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.
342 QRgb* c;
343 int n;
344 if (image.depth()==32) {
345 c = (QRgb*)image.bits();
346 n = image.bytesPerLine() * image.height() / 4;
347 } else {
348 c = image.colorTable();
349 n = image.numColors();
351 while ( n-- && qAlpha(*c++)==0xff )
353 if ( n<0 ) // LIAR!
354 image.setAlphaBuffer(false);
357 png_read_end(png_ptr, end_info);
358 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
360 return image;
362 #endif // QT_NO_IMAGEIO_PNG