Qt 4.8
qpnghandler.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
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 #ifdef QT_USE_BUNDLED_LIBPNG
54 #include <../../3rdparty/libpng/png.h>
55 #include <../../3rdparty/libpng/pngconf.h>
56 #else
57 #include <png.h>
58 #include <pngconf.h>
59 #endif
60 
61 #if PNG_LIBPNG_VER >= 10400 && PNG_LIBPNG_VER <= 10502 \
62  && defined(PNG_PEDANTIC_WARNINGS_SUPPORTED)
63 /*
64  Versions 1.4.0 to 1.5.2 of libpng declare png_longjmp_ptr to
65  have a noreturn attribute if PNG_PEDANTIC_WARNINGS_SUPPORTED
66  is enabled, but most declarations of longjmp in the wild do
67  not add this attribute. This causes problems when the png_jmpbuf
68  macro expands to calling png_set_longjmp_fn with a mismatched
69  longjmp, as compilers such as Clang will treat this as an error.
70 
71  To work around this we override the png_jmpbuf macro to cast
72  longjmp to a png_longjmp_ptr.
73 */
74 # undef png_jmpbuf
75 # ifdef PNG_SETJMP_SUPPORTED
76 # define png_jmpbuf(png_ptr) \
77  (*png_set_longjmp_fn((png_ptr), (png_longjmp_ptr)longjmp, sizeof(jmp_buf)))
78 # else
79 # define png_jmpbuf(png_ptr) \
80  (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)
81 # endif
82 #endif
83 
84 #ifdef Q_OS_WINCE
85 #define CALLBACK_CALL_TYPE __cdecl
86 #else
87 #define CALLBACK_CALL_TYPE
88 #endif
89 
91 
92 #if defined(Q_OS_WINCE) && defined(STANDARDSHELL_UI_MODEL)
93 # define Q_INTERNAL_WIN_NO_THROW __declspec(nothrow)
94 #else
95 # define Q_INTERNAL_WIN_NO_THROW
96 #endif
97 
98 // avoid going through QImage::scanLine() which calls detach
99 #define FAST_SCAN_LINE(data, bpl, y) (data + (y) * bpl)
100 
101 /*
102  All PNG files load to the minimal QImage equivalent.
103 
104  All QImage formats output to reasonably efficient PNG equivalents.
105  Never to grayscale.
106 */
107 
109 {
110 public:
111  enum State {
116  };
117 
119  : gamma(0.0), quality(2), png_ptr(0), info_ptr(0),
120  end_info(0), row_pointers(0), state(Ready), q(qq)
121  { }
122 
123  float gamma;
124  int quality;
127 
128  png_struct *png_ptr;
129  png_info *info_ptr;
130  png_info *end_info;
131  png_byte **row_pointers;
132 
133  bool readPngHeader();
134  bool readPngImage(QImage *image);
135  void readPngTexts(png_info *info);
136 
138 
140 
142 };
143 
144 
145 #if defined(Q_C_CALLBACKS)
146 extern "C" {
147 #endif
148 
150 public:
151  explicit QPNGImageWriter(QIODevice*);
152  ~QPNGImageWriter();
153 
154  enum DisposalMethod { Unspecified, NoDisposal, RestoreBackground, RestoreImage };
155  void setDisposalMethod(DisposalMethod);
156  void setLooping(int loops=0); // 0 == infinity
157  void setFrameDelay(int msecs);
158  void setGamma(float);
159 
160  bool writeImage(const QImage& img, int x, int y);
161  bool writeImage(const QImage& img, int quality, const QString &description, int x, int y);
162  bool writeImage(const QImage& img)
163  { return writeImage(img, 0, 0); }
164  bool writeImage(const QImage& img, int quality, const QString &description)
165  { return writeImage(img, quality, description, 0, 0); }
166 
167  QIODevice* device() { return dev; }
168 
169 private:
173  int looping;
174  int ms_delay;
175  float gamma;
176 };
177 
178 static
179 void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
180 {
181  QPngHandlerPrivate *d = (QPngHandlerPrivate *)png_get_io_ptr(png_ptr);
182  QIODevice *in = d->q->device();
183 
184  if (d->state == QPngHandlerPrivate::ReadingEnd && !in->isSequential() && (in->size() - in->pos()) < 4 && length == 4) {
185  // Workaround for certain malformed PNGs that lack the final crc bytes
186  uchar endcrc[4] = { 0xae, 0x42, 0x60, 0x82 };
187  qMemCopy(data, endcrc, 4);
188  in->seek(in->size());
189  return;
190  }
191 
192  while (length) {
193  int nr = in->read((char*)data, length);
194  if (nr <= 0) {
195  png_error(png_ptr, "Read Error");
196  return;
197  }
198  length -= nr;
199  }
200 }
201 
202 
203 static
204 void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
205 {
206  QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);
207  QIODevice* out = qpiw->device();
208 
209  uint nr = out->write((char*)data, length);
210  if (nr != length) {
211  png_error(png_ptr, "Write Error");
212  return;
213  }
214 }
215 
216 
217 static
218 void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp /* png_ptr */)
219 {
220 }
221 
222 #if defined(Q_C_CALLBACKS)
223 }
224 #endif
225 
226 static
227 void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
228 {
229  if (screen_gamma != 0.0 && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
230  double file_gamma;
231  png_get_gAMA(png_ptr, info_ptr, &file_gamma);
232  png_set_gamma(png_ptr, screen_gamma, file_gamma);
233  }
234 
235  png_uint_32 width;
236  png_uint_32 height;
237  int bit_depth;
238  int color_type;
239  png_bytep trans_alpha = 0;
240  png_color_16p trans_color_p = 0;
241  int num_trans;
242  png_colorp palette = 0;
243  int num_palette;
244  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
245  png_set_interlace_handling(png_ptr);
246 
247  if (color_type == PNG_COLOR_TYPE_GRAY) {
248  // Black & White or 8-bit grayscale
249  if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
250  png_set_invert_mono(png_ptr);
251  png_read_update_info(png_ptr, info_ptr);
252  if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) {
253  image = QImage(width, height, QImage::Format_Mono);
254  if (image.isNull())
255  return;
256  }
257  image.setColorCount(2);
258  image.setColor(1, qRgb(0,0,0));
259  image.setColor(0, qRgb(255,255,255));
260  } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
261  png_set_expand(png_ptr);
262  png_set_strip_16(png_ptr);
263  png_set_gray_to_rgb(png_ptr);
264  if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) {
265  image = QImage(width, height, QImage::Format_ARGB32);
266  if (image.isNull())
267  return;
268  }
269  if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
270  png_set_swap_alpha(png_ptr);
271 
272  png_read_update_info(png_ptr, info_ptr);
273  } else {
274  if (bit_depth == 16)
275  png_set_strip_16(png_ptr);
276  else if (bit_depth < 8)
277  png_set_packing(png_ptr);
278  int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
279  png_read_update_info(png_ptr, info_ptr);
280  if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) {
281  image = QImage(width, height, QImage::Format_Indexed8);
282  if (image.isNull())
283  return;
284  }
285  image.setColorCount(ncols);
286  for (int i=0; i<ncols; i++) {
287  int c = i*255/(ncols-1);
288  image.setColor(i, qRgba(c,c,c,0xff));
289  }
290  if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
291  const int g = trans_color_p->gray;
292  if (g < ncols) {
293  image.setColor(g, 0);
294  }
295  }
296  }
297  } else if (color_type == PNG_COLOR_TYPE_PALETTE
298  && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
299  && num_palette <= 256)
300  {
301  // 1-bit and 8-bit color
302  if (bit_depth != 1)
303  png_set_packing(png_ptr);
304  png_read_update_info(png_ptr, info_ptr);
305  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
307  if (image.size() != QSize(width, height) || image.format() != format) {
308  image = QImage(width, height, format);
309  if (image.isNull())
310  return;
311  }
312  png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
313  image.setColorCount(num_palette);
314  int i = 0;
315  if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
316  while (i < num_trans) {
317  image.setColor(i, qRgba(
318  palette[i].red,
319  palette[i].green,
320  palette[i].blue,
321  trans_alpha[i]
322  )
323  );
324  i++;
325  }
326  }
327  while (i < num_palette) {
328  image.setColor(i, qRgba(
329  palette[i].red,
330  palette[i].green,
331  palette[i].blue,
332  0xff
333  )
334  );
335  i++;
336  }
337  } else {
338  // 32-bit
339  if (bit_depth == 16)
340  png_set_strip_16(png_ptr);
341 
342  png_set_expand(png_ptr);
343 
344  if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
345  png_set_gray_to_rgb(png_ptr);
346 
348  // Only add filler if no alpha, or we can get 5 channel data.
349  if (!(color_type & PNG_COLOR_MASK_ALPHA)
350  && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
351  png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
352  PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
353  // We want 4 bytes, but it isn't an alpha channel
354  format = QImage::Format_RGB32;
355  }
356  if (image.size() != QSize(width, height) || image.format() != format) {
357  image = QImage(width, height, format);
358  if (image.isNull())
359  return;
360  }
361 
362  if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
363  png_set_swap_alpha(png_ptr);
364 
365  png_read_update_info(png_ptr, info_ptr);
366  }
367 
368  // Qt==ARGB==Big(ARGB)==Little(BGRA)
369  if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
370  png_set_bgr(png_ptr);
371  }
372 }
373 
374 
375 #if defined(Q_C_CALLBACKS)
376 extern "C" {
377 #endif
378 static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
379 {
380  qWarning("libpng warning: %s", message);
381 }
382 
383 #if defined(Q_C_CALLBACKS)
384 }
385 #endif
386 
387 
392 {
393 #ifndef QT_NO_IMAGE_TEXT
394  png_textp text_ptr;
395  int num_text=0;
396  png_get_text(png_ptr, info, &text_ptr, &num_text);
397 
398  while (num_text--) {
399  QString key, value;
400  key = QString::fromLatin1(text_ptr->key);
401 #if defined(PNG_iTXt_SUPPORTED)
402  if (text_ptr->itxt_length) {
403  value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length));
404  } else
405 #endif
406  {
407  value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length));
408  }
409  if (!description.isEmpty())
410  description += QLatin1String("\n\n");
411  description += key + QLatin1String(": ") + value.simplified();
412  readTexts.append(key);
413  readTexts.append(value);
414  text_ptr++;
415  }
416 #endif
417 }
418 
419 
424 {
425  state = Error;
426  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
427  if (!png_ptr)
428  return false;
429 
430  png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
431 
432  info_ptr = png_create_info_struct(png_ptr);
433  if (!info_ptr) {
434  png_destroy_read_struct(&png_ptr, 0, 0);
435  png_ptr = 0;
436  return false;
437  }
438 
439  end_info = png_create_info_struct(png_ptr);
440  if (!end_info) {
441  png_destroy_read_struct(&png_ptr, &info_ptr, 0);
442  png_ptr = 0;
443  return false;
444  }
445 
446  if (setjmp(png_jmpbuf(png_ptr))) {
447  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
448  png_ptr = 0;
449  return false;
450  }
451 
452  png_set_read_fn(png_ptr, this, iod_read_fn);
453  png_read_info(png_ptr, info_ptr);
454 
456 
457  state = ReadHeader;
458  return true;
459 }
460 
465 {
466  if (state == Error)
467  return false;
468 
469  if (state == Ready && !readPngHeader()) {
470  state = Error;
471  return false;
472  }
473 
474  row_pointers = 0;
475  if (setjmp(png_jmpbuf(png_ptr))) {
476  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
477  delete [] row_pointers;
478  png_ptr = 0;
479  state = Error;
480  return false;
481  }
482 
483  setup_qt(*outImage, png_ptr, info_ptr, gamma);
484 
485  if (outImage->isNull()) {
486  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
487  delete [] row_pointers;
488  png_ptr = 0;
489  state = Error;
490  return false;
491  }
492 
493  png_uint_32 width = 0;
494  png_uint_32 height = 0;
495  png_int_32 offset_x = 0;
496  png_int_32 offset_y = 0;
497  int bit_depth = 0;
498  int color_type = 0;
499  int unit_type = PNG_OFFSET_PIXEL;
500  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
501  0, 0, 0);
502  png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
503 
504  uchar *data = outImage->bits();
505  int bpl = outImage->bytesPerLine();
506  row_pointers = new png_bytep[height];
507 
508  for (uint y = 0; y < height; y++)
509  row_pointers[y] = data + y * bpl;
510 
511  png_read_image(png_ptr, row_pointers);
512 
513 #if 0 // libpng takes care of this.
514  png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
515  if (outImage->depth()==32 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
516  QRgb trans = 0xFF000000 | qRgb(
517  (info_ptr->trans_values.red << 8 >> bit_depth)&0xff,
518  (info_ptr->trans_values.green << 8 >> bit_depth)&0xff,
519  (info_ptr->trans_values.blue << 8 >> bit_depth)&0xff);
520  for (uint y=0; y<height; y++) {
521  for (uint x=0; x<info_ptr->width; x++) {
522  if (((uint**)jt)[y][x] == trans) {
523  ((uint**)jt)[y][x] &= 0x00FFFFFF;
524  } else {
525  }
526  }
527  }
528  }
529 #endif
530 
531  outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
532  outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
533 
534  if (unit_type == PNG_OFFSET_PIXEL)
535  outImage->setOffset(QPoint(offset_x, offset_y));
536 
537  state = ReadingEnd;
538  png_read_end(png_ptr, end_info);
539 
540 #ifndef QT_NO_IMAGE_TEXT
542  for (int i = 0; i < readTexts.size()-1; i+=2)
543  outImage->setText(readTexts.at(i), readTexts.at(i+1));
544 #endif
545 
546  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
547  delete [] row_pointers;
548  png_ptr = 0;
549  state = Ready;
550 
551  // sanity check palette entries
552  if (color_type == PNG_COLOR_TYPE_PALETTE
553  && outImage->format() == QImage::Format_Indexed8) {
554  int color_table_size = outImage->colorCount();
555  for (int y=0; y<(int)height; ++y) {
556  uchar *p = FAST_SCAN_LINE(data, bpl, y);
557  uchar *end = p + width;
558  while (p < end) {
559  if (*p >= color_table_size)
560  *p = 0;
561  ++p;
562  }
563  }
564  }
565 
566  return true;
567 }
568 
570 {
572  png_uint_32 width, height;
573  int bit_depth, color_type;
574  png_colorp palette;
575  int num_palette;
576  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
577  if (color_type == PNG_COLOR_TYPE_GRAY) {
578  // Black & White or 8-bit grayscale
579  if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
580  format = QImage::Format_Mono;
581  } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
582  format = QImage::Format_ARGB32;
583  } else {
584  format = QImage::Format_Indexed8;
585  }
586  } else if (color_type == PNG_COLOR_TYPE_PALETTE
587  && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
588  && num_palette <= 256)
589  {
590  // 1-bit and 8-bit color
591  if (bit_depth != 1)
592  png_set_packing(png_ptr);
593  png_read_update_info(png_ptr, info_ptr);
594  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
595  format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
596  } else {
597  // 32-bit
598  if (bit_depth == 16)
599  png_set_strip_16(png_ptr);
600 
601  format = QImage::Format_ARGB32;
602  // Only add filler if no alpha, or we can get 5 channel data.
603  if (!(color_type & PNG_COLOR_MASK_ALPHA)
604  && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
605  // We want 4 bytes, but it isn't an alpha channel
606  format = QImage::Format_RGB32;
607  }
608  }
609 
610  return format;
611 }
612 
614  dev(iod),
615  frames_written(0),
616  disposal(Unspecified),
617  looping(-1),
618  ms_delay(-1),
619  gamma(0.0)
620 {
621 }
622 
624 {
625 }
626 
628 {
629  disposal = dm;
630 }
631 
633 {
634  looping = loops;
635 }
636 
638 {
639  ms_delay = msecs;
640 }
641 
643 {
644  gamma = g;
645 }
646 
647 
648 #ifndef QT_NO_IMAGE_TEXT
649 static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,
650  const QString &description)
651 {
653  foreach (const QString &key, image.textKeys()) {
654  if (!key.isEmpty())
655  text.insert(key, image.text(key));
656  }
657  foreach (const QString &pair, description.split(QLatin1String("\n\n"))) {
658  int index = pair.indexOf(QLatin1Char(':'));
659  if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) {
660  QString s = pair.simplified();
661  if (!s.isEmpty())
662  text.insert(QLatin1String("Description"), s);
663  } else {
664  QString key = pair.left(index);
665  if (!key.simplified().isEmpty())
666  text.insert(key, pair.mid(index + 2).simplified());
667  }
668  }
669 
670  if (text.isEmpty())
671  return;
672 
673  png_textp text_ptr = new png_text[text.size()];
674  qMemSet(text_ptr, 0, text.size() * sizeof(png_text));
675 
677  int i = 0;
678  while (it != text.constEnd()) {
679  text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData());
680  bool noCompress = (it.value().length() < 40);
681 
682 #ifdef PNG_iTXt_SUPPORTED
683  bool needsItxt = false;
684  foreach(const QChar c, it.value()) {
685  uchar ch = c.cell();
686  if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) {
687  needsItxt = true;
688  break;
689  }
690  }
691 
692  if (needsItxt) {
693  text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;
694  QByteArray value = it.value().toUtf8();
695  text_ptr[i].text = qstrdup(value.constData());
696  text_ptr[i].itxt_length = value.size();
697  text_ptr[i].lang = const_cast<char*>("UTF-8");
698  text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
699  }
700  else
701 #endif
702  {
703  text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
704  QByteArray value = it.value().toLatin1();
705  text_ptr[i].text = qstrdup(value.constData());
706  text_ptr[i].text_length = value.size();
707  }
708  ++i;
709  ++it;
710  }
711 
712  png_set_text(png_ptr, info_ptr, text_ptr, i);
713  for (i = 0; i < text.size(); ++i) {
714  delete [] text_ptr[i].key;
715  delete [] text_ptr[i].text;
716 #ifdef PNG_iTXt_SUPPORTED
717  delete [] text_ptr[i].lang_key;
718 #endif
719  }
720  delete [] text_ptr;
721 }
722 #endif
723 
724 bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
725 {
726  return writeImage(image, -1, QString(), off_x, off_y);
727 }
728 
729 bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image, int quality_in, const QString &description,
730  int off_x_in, int off_y_in)
731 {
732 #ifdef QT_NO_IMAGE_TEXT
733  Q_UNUSED(description);
734 #endif
735 
736  QPoint offset = image.offset();
737  int off_x = off_x_in + offset.x();
738  int off_y = off_y_in + offset.y();
739 
740  png_structp png_ptr;
741  png_infop info_ptr;
742 
743  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
744  if (!png_ptr) {
745  return false;
746  }
747 
748  png_set_error_fn(png_ptr, 0, 0, qt_png_warning);
749 
750  info_ptr = png_create_info_struct(png_ptr);
751  if (!info_ptr) {
752  png_destroy_write_struct(&png_ptr, 0);
753  return false;
754  }
755 
756  if (setjmp(png_jmpbuf(png_ptr))) {
757  png_destroy_write_struct(&png_ptr, &info_ptr);
758  return false;
759  }
760 
761  int quality = quality_in;
762  if (quality >= 0) {
763  if (quality > 9) {
764  qWarning("PNG: Quality %d out of range", quality);
765  quality = 9;
766  }
767  png_set_compression_level(png_ptr, quality);
768  }
769 
770  png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
771 
772 
773  int color_type = 0;
774  if (image.colorCount())
775  color_type = PNG_COLOR_TYPE_PALETTE;
776  else if (image.hasAlphaChannel())
777  color_type = PNG_COLOR_TYPE_RGB_ALPHA;
778  else
779  color_type = PNG_COLOR_TYPE_RGB;
780 
781  png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
782  image.depth() == 1 ? 1 : 8, // per channel
783  color_type, 0, 0, 0); // sets #channels
784 
785  if (gamma != 0.0) {
786  png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
787  }
788 
789  png_color_8 sig_bit;
790  sig_bit.red = 8;
791  sig_bit.green = 8;
792  sig_bit.blue = 8;
793  sig_bit.alpha = image.hasAlphaChannel() ? 8 : 0;
794  png_set_sBIT(png_ptr, info_ptr, &sig_bit);
795 
796  if (image.format() == QImage::Format_MonoLSB)
797  png_set_packswap(png_ptr);
798 
799  if (image.colorCount()) {
800  // Paletted
801  int num_palette = qMin(256, image.colorCount());
802  png_color palette[256];
803  png_byte trans[256];
804  int num_trans = 0;
805  for (int i=0; i<num_palette; i++) {
806  QRgb rgba=image.color(i);
807  palette[i].red = qRed(rgba);
808  palette[i].green = qGreen(rgba);
809  palette[i].blue = qBlue(rgba);
810  trans[i] = qAlpha(rgba);
811  if (trans[i] < 255) {
812  num_trans = i+1;
813  }
814  }
815  png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
816 
817  if (num_trans) {
818  png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0);
819  }
820  }
821 
822  // Swap ARGB to RGBA (normal PNG format) before saving on
823  // BigEndian machines
824  if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
825  png_set_swap_alpha(png_ptr);
826  }
827 
828  // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless
829  if (QSysInfo::ByteOrder == QSysInfo::LittleEndian
830  && image.format() != QImage::Format_RGB888) {
831  png_set_bgr(png_ptr);
832  }
833 
834  if (off_x || off_y) {
835  png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
836  }
837 
838  if (frames_written > 0)
839  png_set_sig_bytes(png_ptr, 8);
840 
841  if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
842  png_set_pHYs(png_ptr, info_ptr,
843  image.dotsPerMeterX(), image.dotsPerMeterY(),
844  PNG_RESOLUTION_METER);
845  }
846 
847 #ifndef QT_NO_IMAGE_TEXT
848  set_text(image, png_ptr, info_ptr, description);
849 #endif
850  png_write_info(png_ptr, info_ptr);
851 
852  if (image.depth() != 1)
853  png_set_packing(png_ptr);
854 
855  if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888)
856  png_set_filler(png_ptr, 0,
857  QSysInfo::ByteOrder == QSysInfo::BigEndian ?
858  PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
859 
860  if (looping >= 0 && frames_written == 0) {
861  uchar data[13] = "NETSCAPE2.0";
862  // 0123456789aBC
863  data[0xB] = looping%0x100;
864  data[0xC] = looping/0x100;
865  png_write_chunk(png_ptr, (png_byte*)"gIFx", data, 13);
866  }
867  if (ms_delay >= 0 || disposal!=Unspecified) {
868  uchar data[4];
869  data[0] = disposal;
870  data[1] = 0;
871  data[2] = (ms_delay/10)/0x100; // hundredths
872  data[3] = (ms_delay/10)%0x100;
873  png_write_chunk(png_ptr, (png_byte*)"gIFg", data, 4);
874  }
875 
876  int height = image.height();
877  int width = image.width();
878  switch (image.format()) {
879  case QImage::Format_Mono:
885  {
886  png_bytep* row_pointers = new png_bytep[height];
887  for (int y=0; y<height; y++)
888  row_pointers[y] = (png_bytep)image.constScanLine(y);
889  png_write_image(png_ptr, row_pointers);
890  delete [] row_pointers;
891  }
892  break;
893  default:
894  {
896  QImage row;
897  png_bytep row_pointers[1];
898  for (int y=0; y<height; y++) {
899  row = image.copy(0, y, width, 1).convertToFormat(fmt);
900  row_pointers[0] = png_bytep(row.constScanLine(0));
901  png_write_rows(png_ptr, row_pointers, 1);
902  }
903  }
904  break;
905  }
906 
907  png_write_end(png_ptr, info_ptr);
908  frames_written++;
909 
910  png_destroy_write_struct(&png_ptr, &info_ptr);
911 
912  return true;
913 }
914 
915 static bool write_png_image(const QImage &image, QIODevice *device,
916  int quality, float gamma, const QString &description)
917 {
918  QPNGImageWriter writer(device);
919  if (quality >= 0) {
920  quality = qMin(quality, 100);
921  quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0]
922  }
923  writer.setGamma(gamma);
924  return writer.writeImage(image, quality, description);
925 }
926 
928  : d(new QPngHandlerPrivate(this))
929 {
930 }
931 
933 {
934  if (d->png_ptr)
935  png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);
936  delete d;
937 }
938 
940 {
942  return false;
943 
945  setFormat("png");
946  return true;
947  }
948 
949  return false;
950 }
951 
953 {
954  if (!device) {
955  qWarning("QPngHandler::canRead() called with no device");
956  return false;
957  }
958 
959  return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
960 }
961 
963 {
964  if (!canRead())
965  return false;
966  return d->readPngImage(image);
967 }
968 
969 bool QPngHandler::write(const QImage &image)
970 {
971  return write_png_image(image, device(), d->quality, d->gamma, d->description);
972 }
973 
975 {
976  return option == Gamma
977  || option == Description
978  || option == ImageFormat
979  || option == Quality
980  || option == Size;
981 }
982 
984 {
986  return QVariant();
988  return QVariant();
989 
990  if (option == Gamma)
991  return d->gamma;
992  else if (option == Quality)
993  return d->quality;
994  else if (option == Description)
995  return d->description;
996  else if (option == Size)
997  return QSize(png_get_image_width(d->png_ptr, d->info_ptr),
998  png_get_image_height(d->png_ptr, d->info_ptr));
999  else if (option == ImageFormat)
1000  return d->readImageFormat();
1001  return 0;
1002 }
1003 
1005 {
1006  if (option == Gamma)
1007  d->gamma = value.toFloat();
1008  else if (option == Quality)
1009  d->quality = value.toInt();
1010  else if (option == Description)
1011  d->description = value.toString();
1012 }
1013 
1015 {
1016  return "png";
1017 }
1018 
1020 
1021 #endif // QT_NO_IMAGEFORMAT_PNG
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
double d
Definition: qnumeric_p.h:62
Format
The following image formats are available in Qt.
Definition: qimage.h:91
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
Definition: qiodevice.cpp:642
unsigned int QRgb
Definition: qrgb.h:53
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
QImage copy(const QRect &rect=QRect()) const
Returns a sub-area of the image as a new image.
Definition: qimage.cpp:1410
void setColor(int i, QRgb c)
Sets the color at the given index in the color table, to the given to colorValue. ...
Definition: qimage.cpp:1850
QPngHandlerPrivate * d
Definition: qpnghandler_p.h:82
static mach_timebase_info_data_t info
unsigned char c[8]
Definition: qnumeric_p.h:62
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
const Key key(const T &value) const
Returns the first key with value value.
Definition: qmap.h:844
#define it(className, varName)
Q_GUI_EXPORT_INLINE int qAlpha(QRgb rgb)
Definition: qrgb.h:66
void readPngTexts(png_info *info)
QByteArray toUtf8() const Q_REQUIRED_RESULT
Returns a UTF-8 representation of the string as a QByteArray.
Definition: qstring.cpp:4074
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition: qimage.cpp:1542
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
QPngHandler * q
int length() const
Returns the number of characters in this string.
Definition: qstring.h:696
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from...
Definition: qiodevice.cpp:624
static void CALLBACK_CALL_TYPE qpiw_flush_fn(png_structp)
#define FAST_SCAN_LINE(data, bpl, y)
Definition: qpnghandler.cpp:99
QString toString() const
Returns the variant as a QString if the variant has type() String , Bool , ByteArray ...
Definition: qvariant.cpp:2270
int size() const
Returns the number of (key, value) pairs in the map.
Definition: qmap.h:201
bool hasAlphaChannel() const
Returns true if the image has a format that respects the alpha channel, otherwise returns false...
Definition: qimage.cpp:6495
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
int bytesPerLine() const
Returns the number of bytes per image scanline.
Definition: qimage.cpp:1812
int dotsPerMeterY() const
Returns the number of pixels that fit vertically in a physical meter.
Definition: qimage.cpp:5628
The QString class provides a Unicode character string.
Definition: qstring.h:83
void setOption(ImageOption option, const QVariant &value)
Sets the option option with the value value.
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
QPoint offset() const
Returns the number of pixels by which the image is intended to be offset by when positioning relative...
Definition: qimage.cpp:5688
Format format() const
Returns the format of the image.
Definition: qimage.cpp:2305
void setGamma(float)
Q_GUI_EXPORT_INLINE int qRed(QRgb rgb)
Definition: qrgb.h:57
static bool write_png_image(const QImage &image, QIODevice *device, int quality, float gamma, const QString &description)
int toInt(bool *ok=0) const
Returns the variant as an int if the variant has type() Int , Bool , ByteArray , Char ...
Definition: qvariant.cpp:2625
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read...
Definition: qiodevice.cpp:791
unsigned char uchar
Definition: qglobal.h:994
void setOffset(const QPoint &)
Sets the number of pixels by which the image is intended to be offset by when positioning relative to...
Definition: qimage.cpp:5705
bool read(QImage *image)
Read an image from the device, and stores it in image.
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
DisposalMethod disposal
bool writeImage(const QImage &img, int x, int y)
void setColorCount(int)
Resizes the color table to contain colorCount entries.
Definition: qimage.cpp:2275
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
Q_GUI_EXPORT_INLINE QRgb qRgba(int r, int g, int b, int a)
Definition: qrgb.h:72
QImage::Format readImageFormat()
QVariant option(ImageOption option) const
Returns the value assigned to option as a QVariant.
QString left(int n) const Q_REQUIRED_RESULT
Returns a substring that contains the n leftmost characters of the string.
Definition: qstring.cpp:3664
static void CALLBACK_CALL_TYPE qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
void setText(const QString &key, const QString &value)
Sets the image text to the given text and associate it with the given key.
Definition: qimage.cpp:5780
ImageOption
This enum describes the different options supported by QImageIOHandler.
const T value(const Key &key) const
Returns the value associated with the key key.
Definition: qmap.h:499
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
QString text(const QString &key=QString()) const
Returns the image text associated with the given key.
Definition: qimage.cpp:5736
qint64 peek(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, without side effects (i.
Definition: qiodevice.cpp:1563
static QString fromUtf8(const char *, int size=-1)
Returns a QString initialized with the first size bytes of the UTF-8 string str.
Definition: qstring.cpp:4302
Q_CORE_EXPORT void qWarning(const char *,...)
bool readPngImage(QImage *image)
The QImage class provides a hardware-independent image representation that allows direct access to th...
Definition: qimage.h:87
static const char * data(const QByteArray &arr)
unsigned int uint
Definition: qglobal.h:996
QPNGImageWriter(QIODevice *)
int indexOf(QChar c, int from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:2838
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
void setDotsPerMeterY(int)
Sets the number of pixels that fit vertically in a physical meter, to y.
Definition: qimage.cpp:5667
QByteArray toLatin1() const Q_REQUIRED_RESULT
Returns a Latin-1 representation of the string as a QByteArray.
Definition: qstring.cpp:3993
int depth() const
Returns the depth of the image.
Definition: qimage.cpp:1620
The State element defines configurations of objects and properties.
const_iterator constBegin() const
Returns a const STL-style iterator pointing to the first item in the map.
Definition: qmap.h:374
Q_GUI_EXPORT_INLINE int qBlue(QRgb rgb)
Definition: qrgb.h:63
QIODevice * device()
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
Definition: qiodevice.cpp:454
QSize size() const
Returns the size of the image, i.
Definition: qimage.cpp:1587
const uchar * constScanLine(int) const
Returns a pointer to the pixel data at the scanline with index i.
Definition: qimage.cpp:1926
void * qMemCopy(void *dest, const void *src, size_t n)
Definition: qglobal.cpp:2508
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
QString mid(int position, int n=-1) const Q_REQUIRED_RESULT
Returns a string that contains n characters of this string, starting at the specified position index...
Definition: qstring.cpp:3706
uchar * bits()
Returns a pointer to the first pixel data.
Definition: qimage.cpp:1946
const_iterator constEnd() const
Returns a const STL-style iterator pointing to the imaginary item after the last item in the map...
Definition: qmap.h:380
int width() const
Returns the width of the image.
Definition: qimage.cpp:1557
void * qMemSet(void *dest, int c, size_t n)
Definition: qglobal.cpp:2509
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const Q_REQUIRED_RESULT
Returns a copy of the image in the given format.
Definition: qimage.cpp:3966
png_struct * png_ptr
void setDotsPerMeterX(int)
Sets the number of pixels that fit horizontally in a physical meter, to x.
Definition: qimage.cpp:5645
bool supportsOption(ImageOption option) const
Returns true if the QImageIOHandler supports the option option; otherwise returns false...
bool canRead() const
Returns true if an image can be read from the device (i.
int colorCount() const
Returns the size of the color table for the image.
Definition: qimage.cpp:1656
QString simplified() const Q_REQUIRED_RESULT
Returns a string that has whitespace removed from the start and the end, and that has each sequence o...
Definition: qstring.cpp:4415
Q_CORE_EXPORT char * qstrdup(const char *)
bool writeImage(const QImage &img)
static QString fromLatin1(const char *, int size=-1)
Returns a QString initialized with the first size characters of the Latin-1 string str...
Definition: qstring.cpp:4188
bool writeImage(const QImage &img, int quality, const QString &description)
iterator insert(const Key &key, const T &value)
Inserts a new item with the key key and a value of value.
Definition: qmap.h:559
png_byte ** row_pointers
bool write(const QImage &image)
Writes the image image to the assigned device.
Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)
Definition: qrgb.h:69
static void CALLBACK_CALL_TYPE iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
int key
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:53
#define CALLBACK_CALL_TYPE
Definition: qpnghandler.cpp:85
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
bool isEmpty() const
Returns true if the map contains no items; otherwise returns false.
Definition: qmap.h:203
float toFloat(bool *ok=0) const
Returns the variant as a float if the variant has type() Double , QMetaType::Float ...
Definition: qvariant.cpp:2725
uchar cell() const
Returns the cell (least significant byte) of the Unicode character.
Definition: qchar.h:283
QIODevice * dev
void setFrameDelay(int msecs)
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
int height() const
Returns the height of the image.
Definition: qimage.cpp:1572
static void setup_qt(QImage &image, png_structp png_ptr, png_infop info_ptr, float screen_gamma=0.0)
int size() const
Returns the number of bytes in this byte array.
Definition: qbytearray.h:402
int y() const
Returns the y coordinate of this point.
Definition: qpoint.h:131
quint16 index
Q_GUI_EXPORT_INLINE int qGreen(QRgb rgb)
Definition: qrgb.h:60
#define Q_INTERNAL_WIN_NO_THROW
Definition: qpnghandler.cpp:95
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:53
void setDisposalMethod(DisposalMethod)
QStringList split(const QString &sep, SplitBehavior behavior=KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const Q_REQUIRED_RESULT
Splits the string into substrings wherever sep occurs, and returns the list of those strings...
Definition: qstring.cpp:6526
void setLooping(int loops=0)
int x() const
Returns the x coordinate of this point.
Definition: qpoint.h:128
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
Definition: qiodevice.cpp:1342
QByteArray name() const
Use format() instead.
static const KeyPair *const end
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success...
Definition: qiodevice.cpp:659
QStringList readTexts
uchar row() const
Returns the row (most significant byte) of the Unicode character.
Definition: qchar.h:284
int dotsPerMeterX() const
Returns the number of pixels that fit horizontally in a physical meter.
Definition: qimage.cpp:5615
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
QStringList textKeys() const
Returns the text keys for this image.
Definition: qimage.cpp:5724
QRgb color(int i) const
Returns the color in the color table at index i.
Definition: qimage.cpp:1829
QPngHandlerPrivate(QPngHandler *qq)
static void CALLBACK_CALL_TYPE qt_png_warning(png_structp, png_const_charp message)
#define text
Definition: qobjectdefs.h:80
static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr, const QString &description)