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