source: trunk/src/gui/image/qpnghandler.cpp@ 735

Last change on this file since 735 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 28.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qpnghandler_p.h"
43
44#ifndef QT_NO_IMAGEFORMAT_PNG
45#include <qcoreapplication.h>
46#include <qiodevice.h>
47#include <qimage.h>
48#include <qlist.h>
49#include <qtextcodec.h>
50#include <qvariant.h>
51#include <qvector.h>
52
53#include <png.h>
54#include <pngconf.h>
55
56#ifdef Q_OS_WINCE
57#define CALLBACK_CALL_TYPE __cdecl
58#else
59#define CALLBACK_CALL_TYPE
60#endif
61
62QT_BEGIN_NAMESPACE
63
64#if defined(Q_OS_WINCE) && defined(STANDARDSHELL_UI_MODEL)
65# define Q_INTERNAL_WIN_NO_THROW __declspec(nothrow)
66#else
67# define Q_INTERNAL_WIN_NO_THROW
68#endif
69
70// avoid going through QImage::scanLine() which calls detach
71#define FAST_SCAN_LINE(data, bpl, y) (data + (y) * bpl)
72
73/*
74 All PNG files load to the minimal QImage equivalent.
75
76 All QImage formats output to reasonably efficient PNG equivalents.
77 Never to grayscale.
78*/
79
80#if defined(Q_C_CALLBACKS)
81extern "C" {
82#endif
83
84class QPNGImageWriter {
85public:
86 explicit QPNGImageWriter(QIODevice*);
87 ~QPNGImageWriter();
88
89 enum DisposalMethod { Unspecified, NoDisposal, RestoreBackground, RestoreImage };
90 void setDisposalMethod(DisposalMethod);
91 void setLooping(int loops=0); // 0 == infinity
92 void setFrameDelay(int msecs);
93 void setGamma(float);
94
95 bool writeImage(const QImage& img, int x, int y);
96 bool writeImage(const QImage& img, int quality, const QString &description, int x, int y);
97 bool writeImage(const QImage& img)
98 { return writeImage(img, 0, 0); }
99 bool writeImage(const QImage& img, int quality, const QString &description)
100 { return writeImage(img, quality, description, 0, 0); }
101
102 QIODevice* device() { return dev; }
103
104private:
105 QIODevice* dev;
106 int frames_written;
107 DisposalMethod disposal;
108 int looping;
109 int ms_delay;
110 float gamma;
111};
112
113static
114void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
115{
116 QIODevice *in = (QIODevice *)png_get_io_ptr(png_ptr);
117
118 while (length) {
119 int nr = in->read((char*)data, length);
120 if (nr <= 0) {
121 png_error(png_ptr, "Read Error");
122 return;
123 }
124 length -= nr;
125 }
126}
127
128
129static
130void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
131{
132 QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);
133 QIODevice* out = qpiw->device();
134
135 uint nr = out->write((char*)data, length);
136 if (nr != length) {
137 png_error(png_ptr, "Write Error");
138 return;
139 }
140}
141
142
143static
144void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp /* png_ptr */)
145{
146}
147
148#if defined(Q_C_CALLBACKS)
149}
150#endif
151
152static
153void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
154{
155 if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
156 double file_gamma;
157 png_get_gAMA(png_ptr, info_ptr, &file_gamma);
158 png_set_gamma(png_ptr, screen_gamma, file_gamma);
159 }
160
161 png_uint_32 width;
162 png_uint_32 height;
163 int bit_depth;
164 int color_type;
165 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
166
167 if (color_type == PNG_COLOR_TYPE_GRAY) {
168 // Black & White or 8-bit grayscale
169 if (bit_depth == 1 && info_ptr->channels == 1) {
170 png_set_invert_mono(png_ptr);
171 png_read_update_info(png_ptr, info_ptr);
172 if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {
173 image = QImage(width, height, QImage::Format_Mono);
174 if (image.isNull())
175 return;
176 }
177 image.setColorCount(2);
178 image.setColor(1, qRgb(0,0,0));
179 image.setColor(0, qRgb(255,255,255));
180 } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
181 png_set_expand(png_ptr);
182 png_set_strip_16(png_ptr);
183 png_set_gray_to_rgb(png_ptr);
184 if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
185 image = QImage(width, height, QImage::Format_ARGB32);
186 if (image.isNull())
187 return;
188 }
189 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
190 png_set_swap_alpha(png_ptr);
191
192 png_read_update_info(png_ptr, info_ptr);
193 } else {
194 if (bit_depth == 16)
195 png_set_strip_16(png_ptr);
196 else if (bit_depth < 8)
197 png_set_packing(png_ptr);
198 int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
199 png_read_update_info(png_ptr, info_ptr);
200 if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {
201 image = QImage(width, height, QImage::Format_Indexed8);
202 if (image.isNull())
203 return;
204 }
205 image.setColorCount(ncols);
206 for (int i=0; i<ncols; i++) {
207 int c = i*255/(ncols-1);
208 image.setColor(i, qRgba(c,c,c,0xff));
209 }
210 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
211#if PNG_LIBPNG_VER_MAJOR < 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 4)
212 const int g = info_ptr->trans_values.gray;
213#else
214 const int g = info_ptr->trans_color.gray;
215#endif
216 if (g < ncols) {
217 image.setColor(g, 0);
218 }
219 }
220 }
221 } else if (color_type == PNG_COLOR_TYPE_PALETTE
222 && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)
223 && info_ptr->num_palette <= 256)
224 {
225 // 1-bit and 8-bit color
226 if (bit_depth != 1)
227 png_set_packing(png_ptr);
228 png_read_update_info(png_ptr, info_ptr);
229 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
230 QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
231 if (image.size() != QSize(width, height) || image.format() != format) {
232 image = QImage(width, height, format);
233 if (image.isNull())
234 return;
235 }
236 image.setColorCount(info_ptr->num_palette);
237 int i = 0;
238 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
239 while (i < info_ptr->num_trans) {
240 image.setColor(i, qRgba(
241 info_ptr->palette[i].red,
242 info_ptr->palette[i].green,
243 info_ptr->palette[i].blue,
244#if PNG_LIBPNG_VER_MAJOR < 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 4)
245 info_ptr->trans[i]
246#else
247 info_ptr->trans_alpha[i]
248#endif
249 )
250 );
251 i++;
252 }
253 }
254 while (i < info_ptr->num_palette) {
255 image.setColor(i, qRgba(
256 info_ptr->palette[i].red,
257 info_ptr->palette[i].green,
258 info_ptr->palette[i].blue,
259 0xff
260 )
261 );
262 i++;
263 }
264 } else {
265 // 32-bit
266 if (bit_depth == 16)
267 png_set_strip_16(png_ptr);
268
269 png_set_expand(png_ptr);
270
271 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
272 png_set_gray_to_rgb(png_ptr);
273
274 QImage::Format format = QImage::Format_ARGB32;
275 // Only add filler if no alpha, or we can get 5 channel data.
276 if (!(color_type & PNG_COLOR_MASK_ALPHA)
277 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
278 png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
279 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
280 // We want 4 bytes, but it isn't an alpha channel
281 format = QImage::Format_RGB32;
282 }
283 if (image.size() != QSize(width, height) || image.format() != format) {
284 image = QImage(width, height, format);
285 if (image.isNull())
286 return;
287 }
288
289 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
290 png_set_swap_alpha(png_ptr);
291
292 png_read_update_info(png_ptr, info_ptr);
293 }
294
295 // Qt==ARGB==Big(ARGB)==Little(BGRA)
296 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
297 png_set_bgr(png_ptr);
298 }
299}
300
301
302#if defined(Q_C_CALLBACKS)
303extern "C" {
304#endif
305static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
306{
307 qWarning("libpng warning: %s", message);
308}
309
310#if defined(Q_C_CALLBACKS)
311}
312#endif
313
314class QPngHandlerPrivate
315{
316public:
317 enum State {
318 Ready,
319 ReadHeader,
320 Error
321 };
322
323 QPngHandlerPrivate(QPngHandler *qq)
324 : gamma(0.0), quality(2), png_ptr(0), info_ptr(0),
325 end_info(0), row_pointers(0), state(Ready), q(qq)
326 { }
327
328 float gamma;
329 int quality;
330 QString description;
331
332 png_struct *png_ptr;
333 png_info *info_ptr;
334 png_info *end_info;
335 png_byte **row_pointers;
336
337 bool readPngHeader();
338 bool readPngImage(QImage *image);
339
340 QImage::Format readImageFormat();
341
342 State state;
343
344 QPngHandler *q;
345};
346
347/*!
348 \internal
349*/
350bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader()
351{
352 state = Error;
353 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
354 if (!png_ptr)
355 return false;
356
357 png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
358
359 info_ptr = png_create_info_struct(png_ptr);
360 if (!info_ptr) {
361 png_destroy_read_struct(&png_ptr, 0, 0);
362 png_ptr = 0;
363 return false;
364 }
365
366 end_info = png_create_info_struct(png_ptr);
367 if (!end_info) {
368 png_destroy_read_struct(&png_ptr, &info_ptr, 0);
369 png_ptr = 0;
370 return false;
371 }
372
373 if (setjmp(png_jmpbuf(png_ptr))) {
374 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
375 png_ptr = 0;
376 return false;
377 }
378
379 png_set_read_fn(png_ptr, q->device(), iod_read_fn);
380 png_read_info(png_ptr, info_ptr);
381
382#ifndef QT_NO_IMAGE_TEXT
383 png_textp text_ptr;
384 int num_text=0;
385 png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
386
387 while (num_text--) {
388 QString key, value;
389#if defined(PNG_iTXt_SUPPORTED)
390 if (text_ptr->lang) {
391 QTextCodec *codec = QTextCodec::codecForName(text_ptr->lang);
392 if (codec) {
393 key = codec->toUnicode(text_ptr->lang_key);
394 value = codec->toUnicode(QByteArray(text_ptr->text, text_ptr->itxt_length));
395 } else {
396 key = QString::fromLatin1(text_ptr->key);
397 value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length)));
398 }
399 } else
400#endif
401 {
402 key = QString::fromLatin1(text_ptr->key);
403 value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length)));
404 }
405 if (!description.isEmpty())
406 description += QLatin1String("\n\n");
407 description += key + QLatin1String(": ") + value.simplified();
408 text_ptr++;
409 }
410#endif
411
412 state = ReadHeader;
413 return true;
414}
415
416/*!
417 \internal
418*/
419bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngImage(QImage *outImage)
420{
421 if (state == Error)
422 return false;
423
424 if (state == Ready && !readPngHeader()) {
425 state = Error;
426 return false;
427 }
428
429 row_pointers = 0;
430 if (setjmp(png_jmpbuf(png_ptr))) {
431 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
432 delete [] row_pointers;
433 png_ptr = 0;
434 state = Error;
435 return false;
436 }
437
438 setup_qt(*outImage, png_ptr, info_ptr, gamma);
439
440 if (outImage->isNull()) {
441 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
442 delete [] row_pointers;
443 png_ptr = 0;
444 state = Error;
445 return false;
446 }
447
448 png_uint_32 width;
449 png_uint_32 height;
450 int bit_depth;
451 int color_type;
452 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
453 0, 0, 0);
454
455 uchar *data = outImage->bits();
456 int bpl = outImage->bytesPerLine();
457 row_pointers = new png_bytep[height];
458
459 for (uint y = 0; y < height; y++)
460 row_pointers[y] = data + y * bpl;
461
462 png_read_image(png_ptr, row_pointers);
463
464#if 0 // libpng takes care of this.
465 png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
466 if (outImage->depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
467 QRgb trans = 0xFF000000 | qRgb(
468 (info_ptr->trans_values.red << 8 >> bit_depth)&0xff,
469 (info_ptr->trans_values.green << 8 >> bit_depth)&0xff,
470 (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff);
471 for (uint y=0; y<height; y++) {
472 for (uint x=0; x<info_ptr->width; x++) {
473 if (((uint**)jt)[y][x] == trans) {
474 ((uint**)jt)[y][x] &= 0x00FFFFFF;
475 } else {
476 }
477 }
478 }
479 }
480#endif
481
482 outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
483 outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
484
485#ifndef QT_NO_IMAGE_TEXT
486 png_textp text_ptr;
487 int num_text=0;
488 png_get_text(png_ptr,info_ptr,&text_ptr,&num_text);
489 while (num_text--) {
490 outImage->setText(text_ptr->key,0,QString::fromAscii(text_ptr->text));
491 text_ptr++;
492 }
493
494 foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
495 int index = pair.indexOf(QLatin1Char(':'));
496 if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
497 outImage->setText(QLatin1String("Description"), pair.simplified());
498 } else {
499 QString key = pair.left(index);
500 outImage->setText(key, pair.mid(index + 2).simplified());
501 }
502 }
503#endif
504
505 png_read_end(png_ptr, end_info);
506 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
507 delete [] row_pointers;
508 png_ptr = 0;
509 state = Ready;
510
511 // sanity check palette entries
512 if (color_type == PNG_COLOR_TYPE_PALETTE
513 && outImage->format() == QImage::Format_Indexed8) {
514 int color_table_size = outImage->colorCount();
515 for (int y=0; y<(int)height; ++y) {
516 uchar *p = FAST_SCAN_LINE(data, bpl, y);
517 uchar *end = p + width;
518 while (p < end) {
519 if (*p >= color_table_size)
520 *p = 0;
521 ++p;
522 }
523 }
524 }
525
526 return true;
527}
528
529QImage::Format QPngHandlerPrivate::readImageFormat()
530{
531 QImage::Format format = QImage::Format_Invalid;
532 png_uint_32 width, height;
533 int bit_depth, color_type;
534 if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY) {
535 // Black & White or 8-bit grayscale
536 if (info_ptr->bit_depth == 1 && info_ptr->channels == 1) {
537 format = QImage::Format_Mono;
538 } else if (info_ptr->bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
539 format = QImage::Format_ARGB32;
540 } else {
541 format = QImage::Format_Indexed8;
542 }
543 } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE
544 && png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)
545 && info_ptr->num_palette <= 256)
546 {
547 // 1-bit and 8-bit color
548 if (info_ptr->bit_depth != 1)
549 png_set_packing(png_ptr);
550 png_read_update_info(png_ptr, info_ptr);
551 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
552 format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
553 } else {
554 // 32-bit
555 if (info_ptr->bit_depth == 16)
556 png_set_strip_16(png_ptr);
557
558 format = QImage::Format_ARGB32;
559 // Only add filler if no alpha, or we can get 5 channel data.
560 if (!(info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
561 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
562 // We want 4 bytes, but it isn't an alpha channel
563 format = QImage::Format_RGB32;
564 }
565 }
566
567 return format;
568}
569
570QPNGImageWriter::QPNGImageWriter(QIODevice* iod) :
571 dev(iod),
572 frames_written(0),
573 disposal(Unspecified),
574 looping(-1),
575 ms_delay(-1),
576 gamma(0.0)
577{
578}
579
580QPNGImageWriter::~QPNGImageWriter()
581{
582}
583
584void QPNGImageWriter::setDisposalMethod(DisposalMethod dm)
585{
586 disposal = dm;
587}
588
589void QPNGImageWriter::setLooping(int loops)
590{
591 looping = loops;
592}
593
594void QPNGImageWriter::setFrameDelay(int msecs)
595{
596 ms_delay = msecs;
597}
598
599void QPNGImageWriter::setGamma(float g)
600{
601 gamma = g;
602}
603
604
605#ifndef QT_NO_IMAGE_TEXT
606static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,
607 const QString &description)
608{
609 QMap<QString, QString> text;
610 foreach (const QString &key, image.textKeys()) {
611 if (!key.isEmpty())
612 text.insert(key, image.text(key));
613 }
614 foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
615 int index = pair.indexOf(QLatin1Char(':'));
616 if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
617 QString s = pair.simplified();
618 if (!s.isEmpty())
619 text.insert(QLatin1String("Description"), s);
620 } else {
621 QString key = pair.left(index);
622 if (!key.simplified().isEmpty())
623 text.insert(key, pair.mid(index + 2).simplified());
624 }
625 }
626
627 if (text.isEmpty())
628 return;
629
630 png_textp text_ptr = new png_text[text.size()];
631
632 QMap<QString, QString>::ConstIterator it = text.constBegin();
633 int i = 0;
634 while (it != text.constEnd()) {
635 QString t = it.value();
636 if (t.length() < 40)
637 text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
638 else
639 text_ptr[i].compression = PNG_TEXT_COMPRESSION_zTXt;
640 text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData());
641
642#ifndef PNG_iTXt_SUPPORTED
643 QByteArray value = it.value().toLatin1();
644 text_ptr[i].text = qstrdup(value.constData());
645 text_ptr[i].text_length = value.size();
646#else
647 QByteArray value = it.value().toUtf8();
648 text_ptr[i].text = qstrdup(value.constData());
649 text_ptr[i].text_length = 0;
650 text_ptr[i].itxt_length = value.size();
651 text_ptr[i].lang = "UTF-8";
652 text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
653#endif
654 ++i;
655 ++it;
656 }
657
658 png_set_text(png_ptr, info_ptr, text_ptr, i);
659 for (i = 0; i < text.size(); ++i) {
660 delete [] text_ptr[i].key;
661 delete [] text_ptr[i].text;
662#ifdef PNG_iTXt_SUPPORTED
663 delete [] text_ptr[i].lang_key;
664#endif
665 }
666 delete [] text_ptr;
667}
668#endif
669
670bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
671{
672 return writeImage(image, -1, QString(), off_x, off_y);
673}
674
675bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image_in, int quality_in, const QString &description,
676 int off_x_in, int off_y_in)
677{
678#ifdef QT_NO_IMAGE_TEXT
679 Q_UNUSED(description);
680#endif
681
682 QImage image;
683 switch (image_in.format()) {
684 case QImage::Format_ARGB32_Premultiplied:
685 case QImage::Format_ARGB4444_Premultiplied:
686 case QImage::Format_ARGB8555_Premultiplied:
687 case QImage::Format_ARGB8565_Premultiplied:
688 case QImage::Format_ARGB6666_Premultiplied:
689 image = image_in.convertToFormat(QImage::Format_ARGB32);
690 break;
691 case QImage::Format_RGB16:
692 case QImage::Format_RGB444:
693 case QImage::Format_RGB555:
694 case QImage::Format_RGB666:
695 case QImage::Format_RGB888:
696 image = image_in.convertToFormat(QImage::Format_RGB32);
697 break;
698 default:
699 image = image_in;
700 break;
701 }
702
703 QPoint offset = image.offset();
704 int off_x = off_x_in + offset.x();
705 int off_y = off_y_in + offset.y();
706
707 png_structp png_ptr;
708 png_infop info_ptr;
709 png_bytep* row_pointers;
710
711 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
712 if (!png_ptr) {
713 return false;
714 }
715
716 png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
717
718 info_ptr = png_create_info_struct(png_ptr);
719 if (!info_ptr) {
720 png_destroy_write_struct(&png_ptr, 0);
721 return false;
722 }
723
724 if (setjmp(png_jmpbuf(png_ptr))) {
725 png_destroy_write_struct(&png_ptr, &info_ptr);
726 return false;
727 }
728
729 int quality = quality_in;
730 if (quality >= 0) {
731 if (quality > 9) {
732 qWarning("PNG: Quality %d out of range", quality);
733 quality = 9;
734 }
735 png_set_compression_level(png_ptr, quality);
736 }
737
738 if (gamma != 0.0) {
739 png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
740 }
741
742 png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
743
744 info_ptr->channels =
745 (image.depth() == 32)
746 ? (image.format() == QImage::Format_RGB32 ? 3 : 4)
747 : 1;
748
749 png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
750 image.depth() == 1 ? 1 : 8 /* per channel */,
751 image.depth() == 32
752 ? image.format() == QImage::Format_RGB32
753 ? PNG_COLOR_TYPE_RGB
754 : PNG_COLOR_TYPE_RGB_ALPHA
755 : PNG_COLOR_TYPE_PALETTE, 0, 0, 0);
756
757
758 //png_set_sBIT(png_ptr, info_ptr, 8);
759 info_ptr->sig_bit.red = 8;
760 info_ptr->sig_bit.green = 8;
761 info_ptr->sig_bit.blue = 8;
762
763 if (image.format() == QImage::Format_MonoLSB)
764 png_set_packswap(png_ptr);
765
766 png_colorp palette = 0;
767 png_bytep copy_trans = 0;
768 if (image.colorCount()) {
769 // Paletted
770 int num_palette = image.colorCount();
771 palette = new png_color[num_palette];
772 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
773 int* trans = new int[num_palette];
774 int num_trans = 0;
775 for (int i=0; i<num_palette; i++) {
776 QRgb rgb=image.color(i);
777 info_ptr->palette[i].red = qRed(rgb);
778 info_ptr->palette[i].green = qGreen(rgb);
779 info_ptr->palette[i].blue = qBlue(rgb);
780 trans[i] = rgb >> 24;
781 if (trans[i] < 255) {
782 num_trans = i+1;
783 }
784 }
785 if (num_trans) {
786 copy_trans = new png_byte[num_trans];
787 for (int i=0; i<num_trans; i++)
788 copy_trans[i] = trans[i];
789 png_set_tRNS(png_ptr, info_ptr, copy_trans, num_trans, 0);
790 }
791 delete [] trans;
792 }
793
794 if (image.format() != QImage::Format_RGB32) {
795 info_ptr->sig_bit.alpha = 8;
796 }
797
798 // Swap ARGB to RGBA (normal PNG format) before saving on
799 // BigEndian machines
800 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
801 png_set_swap_alpha(png_ptr);
802 }
803
804 // Qt==ARGB==Big(ARGB)==Little(BGRA)
805 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
806 png_set_bgr(png_ptr);
807 }
808
809 if (off_x || off_y) {
810 png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
811 }
812
813 if (frames_written > 0)
814 png_set_sig_bytes(png_ptr, 8);
815
816 if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
817 png_set_pHYs(png_ptr, info_ptr,
818 image.dotsPerMeterX(), image.dotsPerMeterY(),
819 PNG_RESOLUTION_METER);
820 }
821
822#ifndef QT_NO_IMAGE_TEXT
823 set_text(image, png_ptr, info_ptr, description);
824#endif
825 png_write_info(png_ptr, info_ptr);
826
827 if (image.depth() != 1)
828 png_set_packing(png_ptr);
829
830 if (image.format() == QImage::Format_RGB32)
831 png_set_filler(png_ptr, 0,
832 QSysInfo::ByteOrder == QSysInfo::BigEndian ?
833 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
834
835 if (looping >= 0 && frames_written == 0) {
836 uchar data[13] = "NETSCAPE2.0";
837 // 0123456789aBC
838 data[0xB] = looping%0x100;
839 data[0xC] = looping/0x100;
840 png_write_chunk(png_ptr, (png_byte*)"gIFx", data, 13);
841 }
842 if (ms_delay >= 0 || disposal!=Unspecified) {
843 uchar data[4];
844 data[0] = disposal;
845 data[1] = 0;
846 data[2] = (ms_delay/10)/0x100; // hundredths
847 data[3] = (ms_delay/10)%0x100;
848 png_write_chunk(png_ptr, (png_byte*)"gIFg", data, 4);
849 }
850
851 png_uint_32 width;
852 png_uint_32 height;
853 int bit_depth;
854 int color_type;
855 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
856 0, 0, 0);
857
858 const uchar *data = (static_cast<const QImage *>(&image))->bits();
859 int bpl = image.bytesPerLine();
860 row_pointers = new png_bytep[height];
861 uint y;
862 for (y=0; y<height; y++) {
863 row_pointers[y] = (png_bytep)(data + y * bpl);
864 }
865 png_write_image(png_ptr, row_pointers);
866 delete [] row_pointers;
867
868 png_write_end(png_ptr, info_ptr);
869 frames_written++;
870
871 if (palette)
872 delete [] palette;
873 if (copy_trans)
874 delete [] copy_trans;
875
876 png_destroy_write_struct(&png_ptr, &info_ptr);
877
878 return true;
879}
880
881static bool write_png_image(const QImage &image, QIODevice *device,
882 int quality, float gamma, const QString &description)
883{
884 QPNGImageWriter writer(device);
885 if (quality >= 0) {
886 quality = qMin(quality, 100);
887 quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]
888 }
889 writer.setGamma(gamma);
890 return writer.writeImage(image, quality, description);
891}
892
893QPngHandler::QPngHandler()
894 : d(new QPngHandlerPrivate(this))
895{
896}
897
898QPngHandler::~QPngHandler()
899{
900 if (d->png_ptr)
901 png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);
902 delete d;
903}
904
905bool QPngHandler::canRead() const
906{
907 if (d->state == QPngHandlerPrivate::Ready) {
908 if (!canRead(device()))
909 return false;
910 setFormat("png");
911 return true;
912 }
913 return d->state != QPngHandlerPrivate::Error;
914}
915
916bool QPngHandler::canRead(QIODevice *device)
917{
918 if (!device) {
919 qWarning("QPngHandler::canRead() called with no device");
920 return false;
921 }
922
923 return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
924}
925
926bool QPngHandler::read(QImage *image)
927{
928 if (!canRead())
929 return false;
930 return d->readPngImage(image);
931}
932
933bool QPngHandler::write(const QImage &image)
934{
935 return write_png_image(image, device(), d->quality, d->gamma, d->description);
936}
937
938bool QPngHandler::supportsOption(ImageOption option) const
939{
940 return option == Gamma
941 || option == Description
942 || option == ImageFormat
943 || option == Quality
944 || option == Size;
945}
946
947QVariant QPngHandler::option(ImageOption option) const
948{
949 if (d->state == QPngHandlerPrivate::Error)
950 return QVariant();
951 if (d->state == QPngHandlerPrivate::Ready && !d->readPngHeader())
952 return QVariant();
953
954 if (option == Gamma)
955 return d->gamma;
956 else if (option == Quality)
957 return d->quality;
958 else if (option == Description)
959 return d->description;
960 else if (option == Size)
961 return QSize(d->info_ptr->width, d->info_ptr->height);
962 else if (option == ImageFormat)
963 return d->readImageFormat();
964 return 0;
965}
966
967void QPngHandler::setOption(ImageOption option, const QVariant &value)
968{
969 if (option == Gamma)
970 d->gamma = value.toFloat();
971 else if (option == Quality)
972 d->quality = value.toInt();
973 else if (option == Description)
974 d->description = value.toString();
975}
976
977QByteArray QPngHandler::name() const
978{
979 return "png";
980}
981
982QT_END_NAMESPACE
983
984#endif // QT_NO_IMAGEFORMAT_PNG
Note: See TracBrowser for help on using the repository browser.