qpnghandler.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/gui/image/qpnghandler.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>-
4** Copyright (C) 2016 The Qt Company Ltd.-
5** Contact: https://www.qt.io/licensing/-
6**-
7** This file is part of the QtGui module of the Qt Toolkit.-
8**-
9** $QT_BEGIN_LICENSE:LGPL$-
10** Commercial License Usage-
11** Licensees holding valid commercial Qt licenses may use this file in-
12** accordance with the commercial license agreement provided with the-
13** Software or, alternatively, in accordance with the terms contained in-
14** a written agreement between you and The Qt Company. For licensing terms-
15** and conditions see https://www.qt.io/terms-conditions. For further-
16** information use the contact form at https://www.qt.io/contact-us.-
17**-
18** GNU Lesser General Public License Usage-
19** Alternatively, this file may be used under the terms of the GNU Lesser-
20** General Public License version 3 as published by the Free Software-
21** Foundation and appearing in the file LICENSE.LGPL3 included in the-
22** packaging of this file. Please review the following information to-
23** ensure the GNU Lesser General Public License version 3 requirements-
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.-
25**-
26** GNU General Public License Usage-
27** Alternatively, this file may be used under the terms of the GNU-
28** General Public License version 2.0 or (at your option) the GNU General-
29** Public license version 3 or any later version approved by the KDE Free-
30** Qt Foundation. The licenses are as published by the Free Software-
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3-
32** included in the packaging of this file. Please review the following-
33** information to ensure the GNU General Public License requirements will-
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and-
35** https://www.gnu.org/licenses/gpl-3.0.html.-
36**-
37** $QT_END_LICENSE$-
38**-
39****************************************************************************/-
40-
41#include "private/qpnghandler_p.h"-
42-
43#ifndef QT_NO_IMAGEFORMAT_PNG-
44#include <qcoreapplication.h>-
45#include <qiodevice.h>-
46#include <qimage.h>-
47#include <qlist.h>-
48#include <qtextcodec.h>-
49#include <qvariant.h>-
50#include <qvector.h>-
51-
52#include <png.h>-
53#include <pngconf.h>-
54-
55#if PNG_LIBPNG_VER >= 10400 && PNG_LIBPNG_VER <= 10502 \-
56 && defined(PNG_PEDANTIC_WARNINGS_SUPPORTED)-
57/*-
58 Versions 1.4.0 to 1.5.2 of libpng declare png_longjmp_ptr to-
59 have a noreturn attribute if PNG_PEDANTIC_WARNINGS_SUPPORTED-
60 is enabled, but most declarations of longjmp in the wild do-
61 not add this attribute. This causes problems when the png_jmpbuf-
62 macro expands to calling png_set_longjmp_fn with a mismatched-
63 longjmp, as compilers such as Clang will treat this as an error.-
64-
65 To work around this we override the png_jmpbuf macro to cast-
66 longjmp to a png_longjmp_ptr.-
67*/-
68# undef png_jmpbuf-
69# ifdef PNG_SETJMP_SUPPORTED-
70# define png_jmpbuf(png_ptr) \-
71 (*png_set_longjmp_fn((png_ptr), (png_longjmp_ptr)longjmp, sizeof(jmp_buf)))-
72# else-
73# define png_jmpbuf(png_ptr) \-
74 (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)-
75# endif-
76#endif-
77-
78#ifdef Q_OS_WINCE-
79#define CALLBACK_CALL_TYPE __cdecl-
80#else-
81#define CALLBACK_CALL_TYPE-
82#endif-
83-
84QT_BEGIN_NAMESPACE-
85-
86#if defined(Q_OS_WINCE) && defined(STANDARDSHELL_UI_MODEL)-
87# define Q_INTERNAL_WIN_NO_THROW __declspec(nothrow)-
88#else-
89# define Q_INTERNAL_WIN_NO_THROW-
90#endif-
91-
92// avoid going through QImage::scanLine() which calls detach-
93#define FAST_SCAN_LINE(data, bpl, y) (data + (y) * bpl)-
94-
95/*-
96 All PNG files load to the minimal QImage equivalent.-
97-
98 All QImage formats output to reasonably efficient PNG equivalents.-
99*/-
100-
101class QPngHandlerPrivate-
102{-
103public:-
104 enum State {-
105 Ready,-
106 ReadHeader,-
107 ReadingEnd,-
108 Error-
109 };-
110-
111 QPngHandlerPrivate(QPngHandler *qq)-
112 : gamma(0.0), fileGamma(0.0), quality(2), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq)-
113 { }-
114-
115 float gamma;-
116 float fileGamma;-
117 int quality;-
118 QString description;-
119 QSize scaledSize;-
120 QStringList readTexts;-
121-
122 png_struct *png_ptr;-
123 png_info *info_ptr;-
124 png_info *end_info;-
125-
126 bool readPngHeader();-
127 bool readPngImage(QImage *image);-
128 void readPngTexts(png_info *info);-
129-
130 QImage::Format readImageFormat();-
131-
132 struct AllocatedMemoryPointers {-
133 AllocatedMemoryPointers()-
134 : row_pointers(0), accRow(0), inRow(0), outRow(0)-
135 { }-
136 void deallocate()-
137 {-
138 delete [] row_pointers;-
139 row_pointers = 0;-
140 delete [] accRow;-
141 accRow = 0;-
142 delete [] inRow;-
143 inRow = 0;-
144 delete [] outRow;-
145 outRow = 0;-
146 }-
147-
148 png_byte **row_pointers;-
149 quint32 *accRow;-
150 png_byte *inRow;-
151 uchar *outRow;-
152 };-
153-
154 AllocatedMemoryPointers amp;-
155-
156 State state;-
157-
158 QPngHandler *q;-
159};-
160-
161-
162class QPNGImageWriter {-
163public:-
164 explicit QPNGImageWriter(QIODevice*);-
165 ~QPNGImageWriter();-
166-
167 enum DisposalMethod { Unspecified, NoDisposal, RestoreBackground, RestoreImage };-
168 void setDisposalMethod(DisposalMethod);-
169 void setLooping(int loops=0); // 0 == infinity-
170 void setFrameDelay(int msecs);-
171 void setGamma(float);-
172-
173 bool writeImage(const QImage& img, int x, int y);-
174 bool writeImage(const QImage& img, volatile int quality, const QString &description, int x, int y);-
175 bool writeImage(const QImage& img)-
176 { return writeImage(img, 0, 0); }-
177 bool writeImage(const QImage& img, int quality, const QString &description)-
178 { return writeImage(img, quality, description, 0, 0); }-
179-
180 QIODevice* device() { return dev; }-
181-
182private:-
183 QIODevice* dev;-
184 int frames_written;-
185 DisposalMethod disposal;-
186 int looping;-
187 int ms_delay;-
188 float gamma;-
189};-
190-
191extern "C" {-
192static-
193void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)-
194{-
195 QPngHandlerPrivate *d = (QPngHandlerPrivate *)png_get_io_ptr(png_ptr);-
196 QIODevice *in = d->q->device();-
197-
198 if (d->state == QPngHandlerPrivate::ReadingEnd && !in->isSequential() && (in->size() - in->pos()) < 4 && length == 4) {-
199 // Workaround for certain malformed PNGs that lack the final crc bytes-
200 uchar endcrc[4] = { 0xae, 0x42, 0x60, 0x82 };-
201 memcpy(data, endcrc, 4);-
202 in->seek(in->size());-
203 return;-
204 }-
205-
206 while (length) {-
207 int nr = in->read((char*)data, length);-
208 if (nr <= 0) {-
209 png_error(png_ptr, "Read Error");-
210 return;-
211 }-
212 length -= nr;-
213 }-
214}-
215-
216-
217static-
218void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)-
219{-
220 QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);-
221 QIODevice* out = qpiw->device();-
222-
223 uint nr = out->write((char*)data, length);-
224 if (nr != length) {-
225 png_error(png_ptr, "Write Error");-
226 return;-
227 }-
228}-
229-
230-
231static-
232void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp /* png_ptr */)-
233{-
234}-
235-
236}-
237-
238static-
239void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead, float screen_gamma=0.0, float file_gamma=0.0)-
240{-
241 if (screen_gamma != 0.0 && file_gamma != 0.0)-
242 png_set_gamma(png_ptr, 1.0f / screen_gamma, file_gamma);-
243-
244 png_uint_32 width;-
245 png_uint_32 height;-
246 int bit_depth;-
247 int color_type;-
248 png_bytep trans_alpha = 0;-
249 png_color_16p trans_color_p = 0;-
250 int num_trans;-
251 png_colorp palette = 0;-
252 int num_palette;-
253 int interlace_method;-
254 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, 0, 0);-
255 png_set_interlace_handling(png_ptr);-
256-
257 if (color_type == PNG_COLOR_TYPE_GRAY) {-
258 // Black & White or 8-bit grayscale-
259 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {-
260 png_set_invert_mono(png_ptr);-
261 png_read_update_info(png_ptr, info_ptr);-
262 if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {-
263 image = QImage(width, height, QImage::Format_Mono);-
264 if (image.isNull())-
265 return;-
266 }-
267 image.setColorCount(2);-
268 image.setColor(1, qRgb(0,0,0));-
269 image.setColor(0, qRgb(255,255,255));-
270 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {-
271 const int g = trans_color_p->gray;-
272 // the image has white in the first position of the color table,-
273 // black in the second. g is 0 for black, 1 for white.-
274 if (g == 0)-
275 image.setColor(1, qRgba(0, 0, 0, 0));-
276 else if (g == 1)-
277 image.setColor(0, qRgba(255, 255, 255, 0));-
278 }-
279 } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
280 png_set_expand(png_ptr);-
281 png_set_strip_16(png_ptr);-
282 png_set_gray_to_rgb(png_ptr);-
283 if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {-
284 image = QImage(width, height, QImage::Format_ARGB32);-
285 if (image.isNull())-
286 return;-
287 }-
288 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)-
289 png_set_swap_alpha(png_ptr);-
290-
291 png_read_update_info(png_ptr, info_ptr);-
292 } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
293 png_set_expand(png_ptr);-
294 if (image.size() != QSize(width, height) || image.format() != QImage::Format_Grayscale8) {-
295 image = QImage(width, height, QImage::Format_Grayscale8);-
296 if (image.isNull())-
297 return;-
298 }-
299-
300 png_read_update_info(png_ptr, info_ptr);-
301 } else {-
302 if (bit_depth == 16)-
303 png_set_strip_16(png_ptr);-
304 else if (bit_depth < 8)-
305 png_set_packing(png_ptr);-
306 int ncols = bit_depth < 8 ? 1 << bit_depth : 256;-
307 png_read_update_info(png_ptr, info_ptr);-
308 if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {-
309 image = QImage(width, height, QImage::Format_Indexed8);-
310 if (image.isNull())-
311 return;-
312 }-
313 image.setColorCount(ncols);-
314 for (int i=0; i<ncols; i++) {-
315 int c = i*255/(ncols-1);-
316 image.setColor(i, qRgba(c,c,c,0xff));-
317 }-
318 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {-
319 const int g = trans_color_p->gray;-
320 if (g < ncols) {-
321 image.setColor(g, 0);-
322 }-
323 }-
324 }-
325 } else if (color_type == PNG_COLOR_TYPE_PALETTE-
326 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)-
327 && num_palette <= 256)-
328 {-
329 // 1-bit and 8-bit color-
330 if (bit_depth != 1)-
331 png_set_packing(png_ptr);-
332 png_read_update_info(png_ptr, info_ptr);-
333 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);-
334 QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;-
335 if (image.size() != QSize(width, height) || image.format() != format) {-
336 image = QImage(width, height, format);-
337 if (image.isNull())-
338 return;-
339 }-
340 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);-
341 image.setColorCount(num_palette);-
342 int i = 0;-
343 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {-
344 while (i < num_trans) {-
345 image.setColor(i, qRgba(-
346 palette[i].red,-
347 palette[i].green,-
348 palette[i].blue,-
349 trans_alpha[i]-
350 )-
351 );-
352 i++;-
353 }-
354 }-
355 while (i < num_palette) {-
356 image.setColor(i, qRgba(-
357 palette[i].red,-
358 palette[i].green,-
359 palette[i].blue,-
360 0xff-
361 )-
362 );-
363 i++;-
364 }-
365 } else {-
366 // 32-bit-
367 if (bit_depth == 16)-
368 png_set_strip_16(png_ptr);-
369-
370 png_set_expand(png_ptr);-
371-
372 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)-
373 png_set_gray_to_rgb(png_ptr);-
374-
375 QImage::Format format = QImage::Format_ARGB32;-
376 // Only add filler if no alpha, or we can get 5 channel data.-
377 if (!(color_type & PNG_COLOR_MASK_ALPHA)-
378 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
379 png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?-
380 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);-
381 // We want 4 bytes, but it isn't an alpha channel-
382 format = QImage::Format_RGB32;-
383 }-
384 QSize outSize(width,height);-
385 if (!scaledSize.isEmpty() && quint32(scaledSize.width()) <= width &&-
386 quint32(scaledSize.height()) <= height && interlace_method == PNG_INTERLACE_NONE) {-
387 // Do inline downscaling-
388 outSize = scaledSize;-
389 if (doScaledRead)-
390 *doScaledRead = true;-
391 }-
392 if (image.size() != outSize || image.format() != format) {-
393 image = QImage(outSize, format);-
394 if (image.isNull())-
395 return;-
396 }-
397-
398 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)-
399 png_set_swap_alpha(png_ptr);-
400-
401 png_read_update_info(png_ptr, info_ptr);-
402 }-
403-
404 // Qt==ARGB==Big(ARGB)==Little(BGRA)-
405 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {-
406 png_set_bgr(png_ptr);-
407 }-
408}-
409-
410static void read_image_scaled(QImage *outImage, png_structp png_ptr, png_infop info_ptr,-
411 QPngHandlerPrivate::AllocatedMemoryPointers &amp, QSize scaledSize)-
412{-
413-
414 png_uint_32 width = 0;-
415 png_uint_32 height = 0;-
416 png_int_32 offset_x = 0;-
417 png_int_32 offset_y = 0;-
418-
419 int bit_depth = 0;-
420 int color_type = 0;-
421 int unit_type = PNG_OFFSET_PIXEL;-
422 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);-
423 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);-
424 uchar *data = outImage->bits();-
425 int bpl = outImage->bytesPerLine();-
426-
427 if (scaledSize.isEmpty() || !width || !height)-
428 return;-
429-
430 const quint32 iysz = height;-
431 const quint32 ixsz = width;-
432 const quint32 oysz = scaledSize.height();-
433 const quint32 oxsz = scaledSize.width();-
434 const quint32 ibw = 4*width;-
435 amp.accRow = new quint32[ibw];-
436 memset(amp.accRow, 0, ibw*sizeof(quint32));-
437 amp.inRow = new png_byte[ibw];-
438 memset(amp.inRow, 0, ibw*sizeof(png_byte));-
439 amp.outRow = new uchar[ibw];-
440 memset(amp.outRow, 0, ibw*sizeof(uchar));-
441 qint32 rval = 0;-
442 for (quint32 oy=0; oy<oysz; oy++) {-
443 // Store the rest of the previous input row, if any-
444 for (quint32 i=0; i < ibw; i++)-
445 amp.accRow[i] = rval*amp.inRow[i];-
446 // Accumulate the next input rows-
447 for (rval = iysz-rval; rval > 0; rval-=oysz) {-
448 png_read_row(png_ptr, amp.inRow, NULL);-
449 quint32 fact = qMin(oysz, quint32(rval));-
450 for (quint32 i=0; i < ibw; i++)-
451 amp.accRow[i] += fact*amp.inRow[i];-
452 }-
453 rval *= -1;-
454-
455 // We have a full output row, store it-
456 for (quint32 i=0; i < ibw; i++)-
457 amp.outRow[i] = uchar(amp.accRow[i]/iysz);-
458-
459 quint32 a[4] = {0, 0, 0, 0};-
460 qint32 cval = oxsz;-
461 quint32 ix = 0;-
462 for (quint32 ox=0; ox<oxsz; ox++) {-
463 for (quint32 i=0; i < 4; i++)-
464 a[i] = cval * amp.outRow[ix+i];-
465 for (cval = ixsz - cval; cval > 0; cval-=oxsz) {-
466 ix += 4;-
467 if (ix >= ibw)-
468 break; // Safety belt, should not happen-
469 quint32 fact = qMin(oxsz, quint32(cval));-
470 for (quint32 i=0; i < 4; i++)-
471 a[i] += fact * amp.outRow[ix+i];-
472 }-
473 cval *= -1;-
474 for (quint32 i=0; i < 4; i++)-
475 data[(4*ox)+i] = uchar(a[i]/ixsz);-
476 }-
477 data += bpl;-
478 }-
479 amp.deallocate();-
480-
481 outImage->setDotsPerMeterX((png_get_x_pixels_per_meter(png_ptr,info_ptr)*oxsz)/ixsz);-
482 outImage->setDotsPerMeterY((png_get_y_pixels_per_meter(png_ptr,info_ptr)*oysz)/iysz);-
483-
484 if (unit_type == PNG_OFFSET_PIXEL)-
485 outImage->setOffset(QPoint(offset_x*oxsz/ixsz, offset_y*oysz/iysz));-
486-
487}-
488-
489extern "C" {-
490static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)-
491{-
492 qWarning("libpng warning: %s", message);-
493}-
494-
495}-
496-
497-
498void Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngTexts(png_info *info)-
499{-
500 png_textp text_ptr;-
501 int num_text=0;-
502 png_get_text(png_ptr, info, &text_ptr, &num_text);-
503-
504 while (num_text--) {-
505 QString key, value;-
506 key = QString::fromLatin1(text_ptr->key);-
507#if defined(PNG_iTXt_SUPPORTED)-
508 if (text_ptr->itxt_length) {-
509 value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length));-
510 } else-
511#endif-
512 {-
513 value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length));-
514 }-
515 if (!description.isEmpty())-
516 description += QLatin1String("\n\n");-
517 description += key + QLatin1String(": ") + value.simplified();-
518 readTexts.append(key);-
519 readTexts.append(value);-
520 text_ptr++;-
521 }-
522}-
523-
524-
525bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader()-
526{-
527 state = Error;-
528 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);-
529 if (!png_ptr)
!png_ptrDescription
TRUEnever evaluated
FALSEnever evaluated
0
530 return false;
never executed: return false;
0
531-
532 png_set_error_fn(png_ptr, 0, 0, qt_png_warning);-
#if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
533-
534 #endifinfo_ptr = png_create_info_struct(png_ptr);-
535 if (!info_ptr) {
!info_ptrDescription
TRUEnever evaluated
FALSEnever evaluated
0
536 png_destroy_read_struct(&png_ptr, 0, 0);-
537 png_ptr = 0;-
538 return false;
never executed: return false;
0
539 }-
540-
541 end_info = png_create_info_struct(png_ptr);-
542 if (!end_info) {
!end_infoDescription
TRUEnever evaluated
FALSEnever evaluated
0
543 png_destroy_read_struct(&png_ptr, &info_ptr, 0);-
544 png_ptr = 0;-
545 return false;
never executed: return false;
0
546 }-
547-
548 if (setjmp(png_jmpbuf(png_ptr))) {
_setjmp (((png_ptr)->jmpbuf))Description
TRUEnever evaluated
FALSEnever evaluated
0
549 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);-
550 png_ptr = 0;-
551 return false;
never executed: return false;
0
552 }-
553-
554 png_set_read_fn(png_ptr, this, iod_read_fn);-
555 png_read_info(png_ptr, info_ptr);-
556-
557 readPngTexts(info_ptr);-
558-
559 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
png_get_valid(...o_ptr, 0x0001)Description
TRUEnever evaluated
FALSEnever evaluated
0
560 double file_gamma = 0.0;-
561 png_get_gAMA(png_ptr, info_ptr, &file_gamma);-
562 fileGamma = file_gamma;-
563 }
never executed: end of block
0
564-
565 state = ReadHeader;-
566 return true;
never executed: return true;
0
567}-
568-
569bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngImage(QImage *outImage)-
570{-
571 if (state == Error)-
572 return false;-
573-
574 if (state == Ready && !readPngHeader()) {-
575 state = Error;-
576 return false;-
577 }-
578-
579 if (setjmp(png_jmpbuf(png_ptr))) {-
580 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);-
581 png_ptr = 0;-
582 amp.deallocate();-
583 state = Error;-
584 return false;-
585 }-
586-
587 bool doScaledRead = false;-
588 setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead, gamma, fileGamma);-
589-
590 if (outImage->isNull()) {-
591 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);-
592 png_ptr = 0;-
593 amp.deallocate();-
594 state = Error;-
595 return false;-
596 }-
597-
598 if (doScaledRead) {-
599 read_image_scaled(outImage, png_ptr, info_ptr, amp, scaledSize);-
600 } else {-
601 png_uint_32 width = 0;-
602 png_uint_32 height = 0;-
603 png_int_32 offset_x = 0;-
604 png_int_32 offset_y = 0;-
605-
606 int bit_depth = 0;-
607 int color_type = 0;-
608 int unit_type = PNG_OFFSET_PIXEL;-
609 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);-
610 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);-
611 uchar *data = outImage->bits();-
612 int bpl = outImage->bytesPerLine();-
613 amp.row_pointers = new png_bytep[height];-
614-
615 for (uint y = 0; y < height; y++)-
616 amp.row_pointers[y] = data + y * bpl;-
617-
618 png_read_image(png_ptr, amp.row_pointers);-
619 amp.deallocate();-
620-
621 outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));-
622 outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));-
623-
624 if (unit_type == PNG_OFFSET_PIXEL)-
625 outImage->setOffset(QPoint(offset_x, offset_y));-
626-
627 // sanity check palette entries-
628 if (color_type == PNG_COLOR_TYPE_PALETTE && outImage->format() == QImage::Format_Indexed8) {-
629 int color_table_size = outImage->colorCount();-
630 for (int y=0; y<(int)height; ++y) {-
631 uchar *p = FAST_SCAN_LINE(data, bpl, y);-
632 uchar *end = p + width;-
633 while (p < end) {-
634 if (*p >= color_table_size)-
635 *p = 0;-
636 ++p;-
637 }-
638 }-
639 }-
640 }-
641-
642 state = ReadingEnd;-
643 png_read_end(png_ptr, end_info);-
644-
645 readPngTexts(end_info);-
646 for (int i = 0; i < readTexts.size()-1; i+=2)-
647 outImage->setText(readTexts.at(i), readTexts.at(i+1));-
648-
649 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);-
650 png_ptr = 0;-
651 amp.deallocate();-
652 state = Ready;-
653-
654 if (scaledSize.isValid() && outImage->size() != scaledSize)-
655 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);-
656-
657 return true;-
658}-
659-
660QImage::Format QPngHandlerPrivate::readImageFormat()-
661{-
662 QImage::Format format = QImage::Format_Invalid;-
663 png_uint_32 width, height;-
664 int bit_depth, color_type;-
665 png_colorp palette;-
666 int num_palette;-
667 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);-
668 if (color_type == PNG_COLOR_TYPE_GRAY) {-
669 // Black & White or 8-bit grayscale-
670 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {-
671 format = QImage::Format_Mono;-
672 } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
673 format = QImage::Format_ARGB32;-
674 } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
675 format = QImage::Format_Grayscale8;-
676 } else {-
677 format = QImage::Format_Indexed8;-
678 }-
679 } else if (color_type == PNG_COLOR_TYPE_PALETTE-
680 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)-
681 && num_palette <= 256)-
682 {-
683 // 1-bit and 8-bit color-
684 format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;-
685 } else {-
686 // 32-bit-
687 format = QImage::Format_ARGB32;-
688 // Only add filler if no alpha, or we can get 5 channel data.-
689 if (!(color_type & PNG_COLOR_MASK_ALPHA)-
690 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {-
691 // We want 4 bytes, but it isn't an alpha channel-
692 format = QImage::Format_RGB32;-
693 }-
694 }-
695-
696 return format;-
697}-
698-
699QPNGImageWriter::QPNGImageWriter(QIODevice* iod) :-
700 dev(iod),-
701 frames_written(0),-
702 disposal(Unspecified),-
703 looping(-1),-
704 ms_delay(-1),-
705 gamma(0.0)-
706{-
707}-
708-
709QPNGImageWriter::~QPNGImageWriter()-
710{-
711}-
712-
713void QPNGImageWriter::setDisposalMethod(DisposalMethod dm)-
714{-
715 disposal = dm;-
716}-
717-
718void QPNGImageWriter::setLooping(int loops)-
719{-
720 looping = loops;-
721}-
722-
723void QPNGImageWriter::setFrameDelay(int msecs)-
724{-
725 ms_delay = msecs;-
726}-
727-
728void QPNGImageWriter::setGamma(float g)-
729{-
730 gamma = g;-
731}-
732-
733-
734static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,-
735 const QString &description)-
736{-
737 QMap<QString, QString> text;-
738 foreach (const QString &key, image.textKeys()) {-
739 if (!key.isEmpty())
!key.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
740 text.insert(key, image.text(key));
never executed: text.insert(key, image.text(key));
0
741 }
never executed: end of block
0
742 foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {-
743 int index = pair.indexOf(QLatin1Char(':'));-
744 if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
index >= 0Description
TRUEnever evaluated
FALSEnever evaluated
pair.indexOf(Q...(' ')) < indexDescription
TRUEnever evaluated
FALSEnever evaluated
0
745 QString s = pair.simplified();-
746 if (!s.isEmpty())
!s.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
747 text.insert(QLatin1String("Description"), s);
never executed: text.insert(QLatin1String("Description"), s);
0
748 } else {
never executed: end of block
0
749 QString key = pair.left(index);-
750 if (!key.simplified().isEmpty())
!key.simplified().isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
751 text.insert(key, pair.mid(index + 2).simplified());
never executed: text.insert(key, pair.mid(index + 2).simplified());
0
752 }
never executed: end of block
0
753 }-
754-
755 if (text.isEmpty())
text.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
756 return;
never executed: return;
0
757-
758 png_textp text_ptr = new png_text[text.size()];-
759 memset(text_ptr, 0, text.size() * sizeof(png_text));-
760-
761 QMap<QString, QString>::ConstIterator it = text.constBegin();-
762 int i = 0;-
763 while (it != text.constEnd()) {
it != text.constEnd()Description
TRUEnever evaluated
FALSEnever evaluated
0
764 text_ptr[i].key = qstrdup(it.key().leftleftRef(79).toLatin1().constData());-
765 bool noCompress = (it.value().length() < 40);-
766-
767#ifdef PNG_iTXt_SUPPORTED-
768 bool needsItxt = false;-
769 foreach(const QChar c, it.value()) {-
770 uchar ch = c.cell();-
771 if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) {-
772 needsItxt = true;-
773 break;-
774 }-
775 }-
776-
777 if (needsItxt) {-
778 text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;-
779 QByteArray value = it.value().toUtf8();-
780 text_ptr[i].text = qstrdup(value.constData());-
781 text_ptr[i].itxt_length = value.size();-
782 text_ptr[i].lang = const_cast<char*>("UTF-8");-
783 text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());-
784 }-
785 else-
786#endif-
787 {-
788 text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
noCompressDescription
TRUEnever evaluated
FALSEnever evaluated
0
789 QByteArray value = it.value().toLatin1();-
790 text_ptr[i].text = qstrdup(value.constData());-
791 text_ptr[i].text_length = value.size();-
792 }-
793 ++i;-
794 ++it;-
795 }
never executed: end of block
0
796-
797 png_set_text(png_ptr, info_ptr, text_ptr, i);-
798 for (i = 0; i < text.size(); ++i) {
i < text.size()Description
TRUEnever evaluated
FALSEnever evaluated
0
799 delete [] text_ptr[i].key;-
800 delete [] text_ptr[i].text;-
801#ifdef PNG_iTXt_SUPPORTED-
802 delete [] text_ptr[i].lang_key;-
803#endif-
804 }
never executed: end of block
0
805 delete [] text_ptr;-
806}
never executed: end of block
0
807-
808bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)-
809{-
810 return writeImage(image, -1, QString(), off_x, off_y);-
811}-
812-
813bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description,-
814 int off_x_in, int off_y_in)-
815{-
816 QPoint offset = image.offset();-
817 int off_x = off_x_in + offset.x();-
818 int off_y = off_y_in + offset.y();-
819-
820 png_structp png_ptr;-
821 png_infop info_ptr;-
822-
823 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);-
824 if (!png_ptr) {-
825 return false;-
826 }-
827-
828 png_set_error_fn(png_ptr, 0, 0, qt_png_warning);-
829-
830 info_ptr = png_create_info_struct(png_ptr);-
831 if (!info_ptr) {-
832 png_destroy_write_struct(&png_ptr, 0);-
833 return false;-
834 }-
835-
836 if (setjmp(png_jmpbuf(png_ptr))) {-
837 png_destroy_write_struct(&png_ptr, &info_ptr);-
838 return false;-
839 }-
840-
841 int quality = quality_in;-
842 if (quality >= 0) {-
843 if (quality > 9) {-
844 qWarning("PNG: Quality %d out of range", quality);-
845 quality = 9;-
846 }-
847 png_set_compression_level(png_ptr, quality);-
848 }-
849-
850 png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);-
851-
852-
853 int color_type = 0;-
854 if (image.colorCount()) {-
855 if (image.isGrayscale())-
856 color_type = PNG_COLOR_TYPE_GRAY;-
857 else-
858 color_type = PNG_COLOR_TYPE_PALETTE;-
859 }-
860 else if (image.format() == QImage::Format_Grayscale8)-
861 color_type = PNG_COLOR_TYPE_GRAY;-
862 else if (image.hasAlphaChannel())-
863 color_type = PNG_COLOR_TYPE_RGB_ALPHA;-
864 else-
865 color_type = PNG_COLOR_TYPE_RGB;-
866-
867 png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),-
868 image.depth() == 1 ? 1 : 8, // per channel-
869 color_type, 0, 0, 0); // sets #channels-
870-
871 if (gamma != 0.0) {-
872 png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);-
873 }-
874-
875 if (image.format() == QImage::Format_MonoLSB)-
876 png_set_packswap(png_ptr);-
877-
878 if (color_type == PNG_COLOR_TYPE_PALETTE) {-
879 // Paletted-
880 int num_palette = qMin(256, image.colorCount());-
881 png_color palette[256];-
882 png_byte trans[256];-
883 int num_trans = 0;-
884 for (int i=0; i<num_palette; i++) {-
885 QRgb rgba=image.color(i);-
886 palette[i].red = qRed(rgba);-
887 palette[i].green = qGreen(rgba);-
888 palette[i].blue = qBlue(rgba);-
889 trans[i] = qAlpha(rgba);-
890 if (trans[i] < 255) {-
891 num_trans = i+1;-
892 }-
893 }-
894 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);-
895-
896 if (num_trans) {-
897 png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0);-
898 }-
899 }-
900-
901 // Swap ARGB to RGBA (normal PNG format) before saving on-
902 // BigEndian machines-
903 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {-
904 png_set_swap_alpha(png_ptr);-
905 }-
906-
907 // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless-
908 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian-
909 && image.format() != QImage::Format_RGB888) {-
910 png_set_bgr(png_ptr);-
911 }-
912-
913 if (off_x || off_y) {-
914 png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);-
915 }-
916-
917 if (frames_written > 0)-
918 png_set_sig_bytes(png_ptr, 8);-
919-
920 if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {-
921 png_set_pHYs(png_ptr, info_ptr,-
922 image.dotsPerMeterX(), image.dotsPerMeterY(),-
923 PNG_RESOLUTION_METER);-
924 }-
925-
926 set_text(image, png_ptr, info_ptr, description);-
927-
928 png_write_info(png_ptr, info_ptr);-
929-
930 if (image.depth() != 1)-
931 png_set_packing(png_ptr);-
932-
933 if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888)-
934 png_set_filler(png_ptr, 0,-
935 QSysInfo::ByteOrder == QSysInfo::BigEndian ?-
936 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);-
937-
938 if (looping >= 0 && frames_written == 0) {-
939 uchar data[13] = "NETSCAPE2.0";-
940 // 0123456789aBC-
941 data[0xB] = looping%0x100;-
942 data[0xC] = looping/0x100;-
943 png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFx"), data, 13);-
944 }-
945 if (ms_delay >= 0 || disposal!=Unspecified) {-
946 uchar data[4];-
947 data[0] = disposal;-
948 data[1] = 0;-
949 data[2] = (ms_delay/10)/0x100; // hundredths-
950 data[3] = (ms_delay/10)%0x100;-
951 png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFg"), data, 4);-
952 }-
953-
954 int height = image.height();-
955 int width = image.width();-
956 switch (image.format()) {-
957 case QImage::Format_Mono:-
958 case QImage::Format_MonoLSB:-
959 case QImage::Format_Indexed8:-
960 case QImage::Format_Grayscale8:-
961 case QImage::Format_RGB32:-
962 case QImage::Format_ARGB32:-
963 case QImage::Format_RGB888:-
964 {-
965 png_bytep* row_pointers = new png_bytep[height];-
966 for (int y=0; y<height; y++)-
967 row_pointers[y] = const_cast<png_bytep>(image.constScanLine(y));-
968 png_write_image(png_ptr, row_pointers);-
969 delete [] row_pointers;-
970 }-
971 break;-
972 default:-
973 {-
974 QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;-
975 QImage row;-
976 png_bytep row_pointers[1];-
977 for (int y=0; y<height; y++) {-
978 row = image.copy(0, y, width, 1).convertToFormat(fmt);-
979 row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0));-
980 png_write_rows(png_ptr, row_pointers, 1);-
981 }-
982 }-
983 break;-
984 }-
985-
986 png_write_end(png_ptr, info_ptr);-
987 frames_written++;-
988-
989 png_destroy_write_struct(&png_ptr, &info_ptr);-
990-
991 return true;-
992}-
993-
994static bool write_png_image(const QImage &image, QIODevice *device,-
995 int quality, float gamma, const QString &description)-
996{-
997 QPNGImageWriter writer(device);-
998 if (quality >= 0) {-
999 quality = qMin(quality, 100);-
1000 quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]-
1001 }-
1002 writer.setGamma(gamma);-
1003 return writer.writeImage(image, quality, description);-
1004}-
1005-
1006QPngHandler::QPngHandler()-
1007 : d(new QPngHandlerPrivate(this))-
1008{-
1009}-
1010-
1011QPngHandler::~QPngHandler()-
1012{-
1013 if (d->png_ptr)-
1014 png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);-
1015 delete d;-
1016}-
1017-
1018bool QPngHandler::canRead() const-
1019{-
1020 if (d->state == QPngHandlerPrivate::Ready && !canRead(device()))-
1021 return false;-
1022-
1023 if (d->state != QPngHandlerPrivate::Error) {-
1024 setFormat("png");-
1025 return true;-
1026 }-
1027-
1028 return false;-
1029}-
1030-
1031bool QPngHandler::canRead(QIODevice *device)-
1032{-
1033 if (!device) {-
1034 qWarning("QPngHandler::canRead() called with no device");-
1035 return false;-
1036 }-
1037-
1038 return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";-
1039}-
1040-
1041bool QPngHandler::read(QImage *image)-
1042{-
1043 if (!canRead())-
1044 return false;-
1045 return d->readPngImage(image);-
1046}-
1047-
1048bool QPngHandler::write(const QImage &image)-
1049{-
1050 return write_png_image(image, device(), d->quality, d->gamma, d->description);-
1051}-
1052-
1053bool QPngHandler::supportsOption(ImageOption option) const-
1054{-
1055 return option == Gamma-
1056 || option == Description-
1057 || option == ImageFormat-
1058 || option == Quality-
1059 || option == Size-
1060 || option == ScaledSize;-
1061}-
1062-
1063QVariant QPngHandler::option(ImageOption option) const-
1064{-
1065 if (d->state == QPngHandlerPrivate::Error)-
1066 return QVariant();-
1067 if (d->state == QPngHandlerPrivate::Ready && !d->readPngHeader())-
1068 return QVariant();-
1069-
1070 if (option == Gamma)-
1071 return d->gamma == 0.0 ? d->fileGamma : d->gamma;-
1072 else if (option == Quality)-
1073 return d->quality;-
1074 else if (option == Description)-
1075 return d->description;-
1076 else if (option == Size)-
1077 return QSize(png_get_image_width(d->png_ptr, d->info_ptr),-
1078 png_get_image_height(d->png_ptr, d->info_ptr));-
1079 else if (option == ScaledSize)-
1080 return d->scaledSize;-
1081 else if (option == ImageFormat)-
1082 return d->readImageFormat();-
1083 return QVariant();-
1084}-
1085-
1086void QPngHandler::setOption(ImageOption option, const QVariant &value)-
1087{-
1088 if (option == Gamma)-
1089 d->gamma = value.toFloat();-
1090 else if (option == Quality)-
1091 d->quality = value.toInt();-
1092 else if (option == Description)-
1093 d->description = value.toString();-
1094 else if (option == ScaledSize)-
1095 d->scaledSize = value.toSize();-
1096}-
1097-
1098QByteArray QPngHandler::name() const-
1099{-
1100 return "png";-
1101}-
1102-
1103QT_END_NAMESPACE-
1104-
1105#endif // QT_NO_IMAGEFORMAT_PNG-
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9