src/plugins/imageformats/ico/qicohandler.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 plugins 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 /*! 
       
    43     \class QtIcoHandler
       
    44     \since 4.4
       
    45     \brief The QtIcoHandler class provides support for the ICO image format.
       
    46     \internal
       
    47 */
       
    48 
       
    49 
       
    50 
       
    51 #include "qicohandler.h"
       
    52 #include <QtCore/qendian.h>
       
    53 #include <QtGui/QImage>
       
    54 #include <QtCore/QFile>
       
    55 #include <QtCore/QBuffer>
       
    56 // These next two structs represent how the icon information is stored
       
    57 // in an ICO file.
       
    58 typedef struct
       
    59 {
       
    60     quint8  bWidth;               // Width of the image
       
    61     quint8  bHeight;              // Height of the image (times 2)
       
    62     quint8  bColorCount;          // Number of colors in image (0 if >=8bpp) [ not ture ]
       
    63     quint8  bReserved;            // Reserved
       
    64     quint16 wPlanes;              // Color Planes
       
    65     quint16 wBitCount;            // Bits per pixel
       
    66     quint32 dwBytesInRes;         // how many bytes in this resource?
       
    67     quint32 dwImageOffset;        // where in the file is this image
       
    68 } ICONDIRENTRY, *LPICONDIRENTRY;
       
    69 #define ICONDIRENTRY_SIZE 16
       
    70 
       
    71 typedef struct
       
    72 {
       
    73     quint16 idReserved;   // Reserved
       
    74     quint16 idType;       // resource type (1 for icons)
       
    75     quint16 idCount;      // how many images?
       
    76     ICONDIRENTRY    idEntries[1]; // the entries for each image
       
    77 } ICONDIR, *LPICONDIR;
       
    78 #define ICONDIR_SIZE    6       // Exclude the idEntries field
       
    79 
       
    80 typedef struct {                    // BMP information header
       
    81     quint32 biSize;                // size of this struct
       
    82     quint32 biWidth;               // pixmap width
       
    83     quint32 biHeight;              // pixmap height     (specifies the combined height of the XOR and AND masks)
       
    84     quint16 biPlanes;              // should be 1
       
    85     quint16 biBitCount;            // number of bits per pixel
       
    86     quint32 biCompression;         // compression method
       
    87     quint32 biSizeImage;           // size of image
       
    88     quint32 biXPelsPerMeter;       // horizontal resolution
       
    89     quint32 biYPelsPerMeter;       // vertical resolution
       
    90     quint32 biClrUsed;             // number of colors used
       
    91     quint32 biClrImportant;        // number of important colors
       
    92 } BMP_INFOHDR ,*LPBMP_INFOHDR;
       
    93 #define BMP_INFOHDR_SIZE 40
       
    94 
       
    95 class ICOReader
       
    96 {
       
    97 public:
       
    98     ICOReader(QIODevice * iodevice);
       
    99     int count();
       
   100     QImage iconAt(int index);
       
   101     static bool canRead(QIODevice *iodev);
       
   102 
       
   103     static QList<QImage> read(QIODevice * device);
       
   104 
       
   105     static bool write(QIODevice * device, const QList<QImage> & images);
       
   106 
       
   107 private:
       
   108     bool readHeader();
       
   109     bool readIconEntry(int index, ICONDIRENTRY * iconEntry);
       
   110 
       
   111     bool readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header);
       
   112     void findColorInfo(QImage & image);
       
   113     void readColorTable(QImage & image);
       
   114 
       
   115     void readBMP(QImage & image);
       
   116     void read1BitBMP(QImage & image);
       
   117     void read4BitBMP(QImage & image);
       
   118     void read8BitBMP(QImage & image);
       
   119     void read16_24_32BMP(QImage & image);
       
   120 
       
   121     struct IcoAttrib
       
   122     {
       
   123         int nbits;
       
   124         int ncolors;
       
   125         int h;
       
   126         int w;
       
   127         int depth;
       
   128     } icoAttrib;
       
   129 
       
   130     QIODevice * iod;
       
   131     qint64 startpos;
       
   132     bool headerRead;
       
   133     ICONDIR iconDir;
       
   134 
       
   135 };
       
   136 
       
   137 // Data readers and writers that takes care of alignment and endian stuff.
       
   138 static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry)
       
   139 {
       
   140     if (iodev) {
       
   141         uchar tmp[ICONDIRENTRY_SIZE];
       
   142         if (iodev->read((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) {
       
   143             iconDirEntry->bWidth = tmp[0];
       
   144             iconDirEntry->bHeight = tmp[1];
       
   145             iconDirEntry->bColorCount = tmp[2];
       
   146             iconDirEntry->bReserved = tmp[3];
       
   147 
       
   148             iconDirEntry->wPlanes = qFromLittleEndian<quint16>(&tmp[4]);
       
   149             iconDirEntry->wBitCount = qFromLittleEndian<quint16>(&tmp[6]);
       
   150             iconDirEntry->dwBytesInRes = qFromLittleEndian<quint32>(&tmp[8]);
       
   151             iconDirEntry->dwImageOffset = qFromLittleEndian<quint32>(&tmp[12]);
       
   152             return true;
       
   153         }
       
   154     }
       
   155     return false;
       
   156 }
       
   157 
       
   158 static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry)
       
   159 {
       
   160     if (iodev) {
       
   161         uchar tmp[ICONDIRENTRY_SIZE];
       
   162         tmp[0] = iconEntry.bWidth;
       
   163         tmp[1] = iconEntry.bHeight;
       
   164         tmp[2] = iconEntry.bColorCount;
       
   165         tmp[3] = iconEntry.bReserved;
       
   166         qToLittleEndian<quint16>(iconEntry.wPlanes, &tmp[4]);
       
   167         qToLittleEndian<quint16>(iconEntry.wBitCount, &tmp[6]);
       
   168         qToLittleEndian<quint32>(iconEntry.dwBytesInRes, &tmp[8]);
       
   169         qToLittleEndian<quint32>(iconEntry.dwImageOffset, &tmp[12]);
       
   170         return (iodev->write((char*)tmp,  ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) ? true : false;
       
   171     }
       
   172 
       
   173     return false;
       
   174 }
       
   175 
       
   176 static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir)
       
   177 {
       
   178     if (iodev) {
       
   179         uchar tmp[ICONDIR_SIZE];
       
   180         if (iodev->read((char*)tmp, ICONDIR_SIZE) == ICONDIR_SIZE) {
       
   181             iconDir->idReserved = qFromLittleEndian<quint16>(&tmp[0]);
       
   182             iconDir->idType = qFromLittleEndian<quint16>(&tmp[2]);
       
   183             iconDir->idCount = qFromLittleEndian<quint16>(&tmp[4]);
       
   184             return true;
       
   185         }
       
   186     }
       
   187     return false;
       
   188 }
       
   189 
       
   190 static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir)
       
   191 {
       
   192     if (iodev) {
       
   193         uchar tmp[6];
       
   194         qToLittleEndian(iconDir.idReserved, tmp);
       
   195         qToLittleEndian(iconDir.idType, &tmp[2]);
       
   196         qToLittleEndian(iconDir.idCount, &tmp[4]);
       
   197         return (iodev->write((char*)tmp,  6) == 6) ? true : false;
       
   198     }
       
   199     return false;
       
   200 }
       
   201 
       
   202 static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)
       
   203 {
       
   204     if (iodev) {
       
   205         uchar header[BMP_INFOHDR_SIZE];
       
   206         if (iodev->read((char*)header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) {
       
   207             pHeader->biSize = qFromLittleEndian<quint32>(&header[0]);
       
   208             pHeader->biWidth = qFromLittleEndian<quint32>(&header[4]);
       
   209             pHeader->biHeight = qFromLittleEndian<quint32>(&header[8]);
       
   210             pHeader->biPlanes = qFromLittleEndian<quint16>(&header[12]);
       
   211             pHeader->biBitCount = qFromLittleEndian<quint16>(&header[14]);
       
   212             pHeader->biCompression = qFromLittleEndian<quint32>(&header[16]);
       
   213             pHeader->biSizeImage = qFromLittleEndian<quint32>(&header[20]);
       
   214             pHeader->biXPelsPerMeter = qFromLittleEndian<quint32>(&header[24]);
       
   215             pHeader->biYPelsPerMeter = qFromLittleEndian<quint32>(&header[28]);
       
   216             pHeader->biClrUsed = qFromLittleEndian<quint32>(&header[32]);
       
   217             pHeader->biClrImportant = qFromLittleEndian<quint32>(&header[36]);
       
   218             return true;
       
   219         }
       
   220     }
       
   221     return false;
       
   222 }
       
   223 
       
   224 static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header)
       
   225 {
       
   226     if (iodev) {
       
   227         uchar tmp[BMP_INFOHDR_SIZE];
       
   228         qToLittleEndian<quint32>(header.biSize, &tmp[0]);
       
   229         qToLittleEndian<quint32>(header.biWidth, &tmp[4]);
       
   230         qToLittleEndian<quint32>(header.biHeight, &tmp[8]);
       
   231         qToLittleEndian<quint16>(header.biPlanes, &tmp[12]);
       
   232         qToLittleEndian<quint16>(header.biBitCount, &tmp[14]);
       
   233         qToLittleEndian<quint32>(header.biCompression, &tmp[16]);
       
   234         qToLittleEndian<quint32>(header.biSizeImage, &tmp[20]);
       
   235         qToLittleEndian<quint32>(header.biXPelsPerMeter, &tmp[24]);
       
   236         qToLittleEndian<quint32>(header.biYPelsPerMeter, &tmp[28]);
       
   237         qToLittleEndian<quint32>(header.biClrUsed, &tmp[32]);
       
   238         qToLittleEndian<quint32>(header.biClrImportant, &tmp[36]);
       
   239 
       
   240         return (iodev->write((char*)tmp, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) ? true : false;
       
   241     }
       
   242     return false;
       
   243 }
       
   244 
       
   245 
       
   246 ICOReader::ICOReader(QIODevice * iodevice)
       
   247 : iod(iodevice)
       
   248 , startpos(0)
       
   249 , headerRead(false)
       
   250 {
       
   251 }
       
   252 
       
   253 
       
   254 int ICOReader::count()
       
   255 {
       
   256     if (readHeader())
       
   257         return iconDir.idCount;
       
   258     return 0;
       
   259 }
       
   260 
       
   261 bool ICOReader::canRead(QIODevice *iodev)
       
   262 {
       
   263     bool isProbablyICO = false;
       
   264     if (iodev) {
       
   265         qint64 oldPos = iodev->pos();
       
   266 
       
   267         ICONDIR ikonDir;
       
   268         if (readIconDir(iodev, &ikonDir)) {
       
   269             qint64 readBytes = ICONDIR_SIZE;
       
   270             if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) {
       
   271                 readBytes += ICONDIRENTRY_SIZE;
       
   272                 // ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file.
       
   273                 if (   ikonDir.idReserved == 0
       
   274                     && ikonDir.idType == 1
       
   275                     && ikonDir.idEntries[0].bReserved == 0
       
   276                     && ikonDir.idEntries[0].wPlanes <= 1
       
   277                     && ikonDir.idEntries[0].wBitCount <= 32     // Bits per pixel
       
   278                     && ikonDir.idEntries[0].dwBytesInRes >= 40  // Must be over 40, since sizeof (infoheader) == 40
       
   279                     ) {
       
   280                     isProbablyICO = true;
       
   281                 }
       
   282 
       
   283                 if (iodev->isSequential()) {
       
   284                     // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
       
   285                     quint32 tmp = ikonDir.idEntries[0].dwImageOffset;
       
   286                     iodev->ungetChar((tmp >> 24) & 0xff);
       
   287                     iodev->ungetChar((tmp >> 16) & 0xff);
       
   288                     iodev->ungetChar((tmp >>  8) & 0xff);
       
   289                     iodev->ungetChar(tmp & 0xff);
       
   290 
       
   291                     tmp = ikonDir.idEntries[0].dwBytesInRes;
       
   292                     iodev->ungetChar((tmp >> 24) & 0xff);
       
   293                     iodev->ungetChar((tmp >> 16) & 0xff);
       
   294                     iodev->ungetChar((tmp >>  8) & 0xff);
       
   295                     iodev->ungetChar(tmp & 0xff);
       
   296 
       
   297                     tmp = ikonDir.idEntries[0].wBitCount;
       
   298                     iodev->ungetChar((tmp >>  8) & 0xff);
       
   299                     iodev->ungetChar(tmp & 0xff);
       
   300 
       
   301                     tmp = ikonDir.idEntries[0].wPlanes;
       
   302                     iodev->ungetChar((tmp >>  8) & 0xff);
       
   303                     iodev->ungetChar(tmp & 0xff);
       
   304 
       
   305                     iodev->ungetChar(ikonDir.idEntries[0].bReserved);
       
   306                     iodev->ungetChar(ikonDir.idEntries[0].bColorCount);
       
   307                     iodev->ungetChar(ikonDir.idEntries[0].bHeight);
       
   308                     iodev->ungetChar(ikonDir.idEntries[0].bWidth);
       
   309                 }
       
   310             }
       
   311 
       
   312             if (iodev->isSequential()) {
       
   313                 // Our structs might be padded due to alignment, so we need to fetch each member before we ungetChar() !
       
   314                 quint32 tmp = ikonDir.idCount;
       
   315                 iodev->ungetChar((tmp >>  8) & 0xff);
       
   316                 iodev->ungetChar(tmp & 0xff);
       
   317 
       
   318                 tmp = ikonDir.idType;
       
   319                 iodev->ungetChar((tmp >>  8) & 0xff);
       
   320                 iodev->ungetChar(tmp & 0xff);
       
   321 
       
   322                 tmp = ikonDir.idReserved;
       
   323                 iodev->ungetChar((tmp >>  8) & 0xff);
       
   324                 iodev->ungetChar(tmp & 0xff);
       
   325             }
       
   326         }
       
   327         if (!iodev->isSequential()) iodev->seek(oldPos);
       
   328     }
       
   329 
       
   330     return isProbablyICO;
       
   331 }
       
   332 
       
   333 bool ICOReader::readHeader()
       
   334 {
       
   335     if (iod && !headerRead) {
       
   336         startpos = iod->pos();
       
   337         if (readIconDir(iod, &iconDir)) {
       
   338             if (iconDir.idReserved == 0 || iconDir.idType == 1)
       
   339             headerRead = true;
       
   340         }
       
   341     }
       
   342 
       
   343     return headerRead;
       
   344 }
       
   345 
       
   346 bool ICOReader::readIconEntry(int index, ICONDIRENTRY *iconEntry)
       
   347 {
       
   348     if (iod) {
       
   349         if (iod->seek(startpos + ICONDIR_SIZE + (index * ICONDIRENTRY_SIZE))) {
       
   350             return readIconDirEntry(iod, iconEntry);
       
   351         }
       
   352     }
       
   353     return false;
       
   354 }
       
   355 
       
   356 
       
   357 
       
   358 bool ICOReader::readBMPHeader(quint32 imageOffset, BMP_INFOHDR * header)
       
   359 {
       
   360     if (iod) {
       
   361         if (iod->seek(startpos + imageOffset)) {
       
   362             if (readBMPInfoHeader(iod, header)) {
       
   363                 return TRUE;
       
   364             }
       
   365         }
       
   366     }
       
   367     return FALSE;
       
   368 }
       
   369 
       
   370 void ICOReader::findColorInfo(QImage & image)
       
   371 {
       
   372     if (icoAttrib.ncolors > 0) {                // set color table
       
   373         readColorTable(image);
       
   374     } else if (icoAttrib.nbits == 16) { // don't support RGB values for 15/16 bpp
       
   375         image = QImage();
       
   376     }
       
   377 }
       
   378 
       
   379 void ICOReader::readColorTable(QImage & image)
       
   380 {
       
   381     if (iod) {
       
   382         image.setNumColors(icoAttrib.ncolors);
       
   383         uchar rgb[4];
       
   384         for (int i=0; i<icoAttrib.ncolors; i++) {
       
   385             if (iod->read((char*)rgb, 4) != 4) {
       
   386             image = QImage();
       
   387             break;
       
   388             }
       
   389             image.setColor(i, qRgb(rgb[2],rgb[1],rgb[0]));
       
   390         }
       
   391     } else {
       
   392         image = QImage();
       
   393     }
       
   394 }
       
   395 
       
   396 void ICOReader::readBMP(QImage & image)
       
   397 {
       
   398     if (icoAttrib.nbits == 1) {                // 1 bit BMP image
       
   399         read1BitBMP(image);
       
   400     } else if (icoAttrib.nbits == 4) {            // 4 bit BMP image
       
   401         read4BitBMP(image);
       
   402     } else if (icoAttrib.nbits == 8) {
       
   403         read8BitBMP(image);
       
   404     } else if (icoAttrib.nbits == 16 || icoAttrib.nbits == 24 || icoAttrib.nbits == 32 ) { // 16,24,32 bit BMP image
       
   405         read16_24_32BMP(image);
       
   406     }
       
   407 }
       
   408 
       
   409 
       
   410 /**
       
   411  * NOTE: A 1 bit BMP is only flipped vertically, and not horizontally like all other color depths!
       
   412  * (This is the same with the bitmask)
       
   413  *
       
   414  */
       
   415 void ICOReader::read1BitBMP(QImage & image)
       
   416 {
       
   417     if (iod) {
       
   418 
       
   419         int h = image.height();
       
   420         int bpl = image.bytesPerLine();
       
   421 
       
   422         while (--h >= 0) {
       
   423             if (iod->read((char*)image.scanLine(h),bpl) != bpl) {
       
   424                 image = QImage();
       
   425                 break;
       
   426             }
       
   427         }
       
   428     } else {
       
   429         image = QImage();
       
   430     }
       
   431 }
       
   432 
       
   433 void ICOReader::read4BitBMP(QImage & image)
       
   434 {
       
   435     if (iod) {
       
   436 
       
   437         int h = icoAttrib.h;
       
   438         int buflen = ((icoAttrib.w+7)/8)*4;
       
   439         uchar *buf = new uchar[buflen];
       
   440         Q_CHECK_PTR(buf);
       
   441 
       
   442         while (--h >= 0) {
       
   443             if (iod->read((char*)buf,buflen) != buflen) {
       
   444                 image = QImage();
       
   445                 break;
       
   446             }
       
   447             register uchar *p = image.scanLine(h);
       
   448             uchar *b = buf;
       
   449             for (int i=0; i<icoAttrib.w/2; i++) {   // convert nibbles to bytes
       
   450                 *p++ = *b >> 4;
       
   451                 *p++ = *b++ & 0x0f;
       
   452             }
       
   453             if (icoAttrib.w & 1)                    // the last nibble
       
   454                 *p = *b >> 4;
       
   455         }
       
   456 
       
   457         delete [] buf;
       
   458 
       
   459     } else {
       
   460         image = QImage();
       
   461     }
       
   462 }
       
   463 
       
   464 void ICOReader::read8BitBMP(QImage & image)
       
   465 {
       
   466     if (iod) {
       
   467 
       
   468         int h = icoAttrib.h;
       
   469         int bpl = image.bytesPerLine();
       
   470 
       
   471         while (--h >= 0) {
       
   472             if (iod->read((char *)image.scanLine(h), bpl) != bpl) {
       
   473                 image = QImage();
       
   474                 break;
       
   475             }
       
   476         }
       
   477     } else {
       
   478         image = QImage();
       
   479     }
       
   480 }
       
   481 
       
   482 void ICOReader::read16_24_32BMP(QImage & image)
       
   483 {
       
   484     if (iod) {
       
   485         int h = icoAttrib.h;
       
   486         register QRgb *p;
       
   487         QRgb  *end;
       
   488         uchar *buf = new uchar[image.bytesPerLine()];
       
   489         int    bpl = ((icoAttrib.w*icoAttrib.nbits+31)/32)*4;
       
   490         uchar *b;
       
   491 
       
   492         while (--h >= 0) {
       
   493             p = (QRgb *)image.scanLine(h);
       
   494             end = p + icoAttrib.w;
       
   495             if (iod->read((char *)buf, bpl) != bpl) {
       
   496                 image = QImage();
       
   497                 break;
       
   498             }
       
   499             b = buf;
       
   500             while (p < end) {
       
   501                 if (icoAttrib.nbits == 24)
       
   502                     *p++ = qRgb(*(b+2), *(b+1), *b);
       
   503                 else if (icoAttrib.nbits == 32)
       
   504                     *p++ = qRgba(*(b+2), *(b+1), *b, *(b+3));
       
   505                 b += icoAttrib.nbits/8;
       
   506             }
       
   507         }
       
   508 
       
   509         delete[] buf;
       
   510 
       
   511     } else {
       
   512         image = QImage();
       
   513     }
       
   514 }
       
   515 
       
   516 QImage ICOReader::iconAt(int index)
       
   517 {
       
   518     QImage img;
       
   519 
       
   520     if (count() > index) { // forces header to be read
       
   521 
       
   522         ICONDIRENTRY iconEntry;
       
   523         if (readIconEntry(index, &iconEntry)) {
       
   524 
       
   525             static const uchar pngMagicData[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
       
   526 
       
   527             iod->seek(iconEntry.dwImageOffset);
       
   528 
       
   529             const QByteArray pngMagic = QByteArray::fromRawData((char*)pngMagicData, sizeof(pngMagicData));
       
   530             const bool isPngImage = (iod->read(pngMagic.size()) == pngMagic);
       
   531 
       
   532             if (isPngImage) {
       
   533                 iod->seek(iconEntry.dwImageOffset);
       
   534                 return QImage::fromData(iod->read(iconEntry.dwBytesInRes), "png");
       
   535             }
       
   536 
       
   537             BMP_INFOHDR header;
       
   538             if (readBMPHeader(iconEntry.dwImageOffset, &header)) {
       
   539                 icoAttrib.nbits = header.biBitCount ? header.biBitCount : iconEntry.wBitCount;
       
   540 
       
   541                 switch (icoAttrib.nbits) {
       
   542                 case 32:
       
   543                 case 24:
       
   544                 case 16:
       
   545                     icoAttrib.depth = 32;
       
   546                     break;
       
   547                 case 8:
       
   548                 case 4:
       
   549                     icoAttrib.depth = 8;
       
   550                     break;
       
   551                 default:
       
   552                     icoAttrib.depth = 1;
       
   553                 }
       
   554                 if (icoAttrib.depth == 32)                // there's no colormap
       
   555                     icoAttrib.ncolors = 0;
       
   556                 else                    // # colors used
       
   557                     icoAttrib.ncolors = header.biClrUsed ? header.biClrUsed : 1 << icoAttrib.nbits;
       
   558                 icoAttrib.w = iconEntry.bWidth;
       
   559                 icoAttrib.h = iconEntry.bHeight;
       
   560 
       
   561                 QImage::Format format = QImage::Format_ARGB32;
       
   562                 if (icoAttrib.nbits == 24)
       
   563                     format = QImage::Format_RGB32;
       
   564                 else if (icoAttrib.ncolors == 2)
       
   565                     format = QImage::Format_Mono;
       
   566                 else if (icoAttrib.ncolors > 0)
       
   567                     format = QImage::Format_Indexed8;
       
   568 
       
   569                 QImage image(icoAttrib.w, icoAttrib.h, format);
       
   570                 if (!image.isNull()) {
       
   571                     findColorInfo(image);
       
   572                     if (!image.isNull()) {
       
   573                         readBMP(image);
       
   574                         if (!image.isNull()) {
       
   575                             QImage mask(image.width(), image.height(), QImage::Format_Mono);
       
   576                             if (!mask.isNull()) {
       
   577                                 mask.setNumColors(2);
       
   578                                 mask.setColor(0, qRgba(255,255,255,0xff));
       
   579                                 mask.setColor(1, qRgba(0  ,0  ,0  ,0xff));
       
   580                                 read1BitBMP(mask);
       
   581                                 if (!mask.isNull()) {
       
   582                                     img = QImage(image.width(), image.height(), QImage::Format_ARGB32 );
       
   583                                     img = image;
       
   584                                     img.setAlphaChannel(mask);
       
   585                                     // (Luckily, it seems that setAlphaChannel() does not ruin the alpha values
       
   586                                     // of partially transparent pixels in those icons that have that)
       
   587                                 }
       
   588                             }
       
   589                         }
       
   590                     }
       
   591                 }
       
   592             }
       
   593         }
       
   594     }
       
   595 
       
   596     return img;
       
   597 }
       
   598 
       
   599 
       
   600 /*!
       
   601     Reads all the icons from the given \a device, and returns them as
       
   602     a list of QImage objects.
       
   603 
       
   604     Each image has an alpha channel that represents the mask from the
       
   605     corresponding icon.
       
   606 
       
   607     \sa write()
       
   608 */
       
   609 QList<QImage> ICOReader::read(QIODevice * device)
       
   610 {
       
   611     QList<QImage> images;
       
   612 
       
   613     ICOReader reader(device);
       
   614     for (int i = 0; i < reader.count(); i++)
       
   615         images += reader.iconAt(i);
       
   616 
       
   617     return images;
       
   618 }
       
   619 
       
   620 
       
   621 /*!
       
   622     Writes all the QImages in the \a images list to the given \a
       
   623     device. Returns true if the images are written successfully;
       
   624     otherwise returns false.
       
   625 
       
   626     The first image in the list is stored as the first icon in the
       
   627     device, and is therefore used as the default icon by applications.
       
   628     The alpha channel of each image is converted to a mask for each
       
   629     corresponding icon.
       
   630 
       
   631     \sa read()
       
   632 */
       
   633 bool ICOReader::write(QIODevice * device, const QList<QImage> & images)
       
   634 {
       
   635     bool retValue = false;
       
   636 
       
   637     if (images.count()) {
       
   638 
       
   639         qint64 origOffset = device->pos();
       
   640 
       
   641         ICONDIR id;
       
   642         id.idReserved = 0;
       
   643         id.idType = 1;
       
   644         id.idCount = images.count();
       
   645 
       
   646         ICONDIRENTRY * entries = new ICONDIRENTRY[id.idCount];
       
   647         BMP_INFOHDR * bmpHeaders = new BMP_INFOHDR[id.idCount];
       
   648         QByteArray * imageData = new QByteArray[id.idCount];
       
   649 
       
   650         for (int i=0; i<id.idCount; i++) {
       
   651 
       
   652             QImage image = images[i];
       
   653             // Scale down the image if it is larger than 128 pixels in either width or height
       
   654             if (image.width() > 128 || image.height() > 128)
       
   655             {
       
   656                 image = image.scaled(128, 128, Qt::KeepAspectRatio, Qt::SmoothTransformation);
       
   657             }
       
   658             QImage maskImage(image.width(), image.height(), QImage::Format_Mono);
       
   659             image = image.convertToFormat(QImage::Format_ARGB32);
       
   660 
       
   661             if (image.hasAlphaChannel()) {
       
   662                 maskImage = image.createAlphaMask();
       
   663             } else {
       
   664                 maskImage.fill(0xff);
       
   665             }
       
   666             maskImage = maskImage.convertToFormat(QImage::Format_Mono);
       
   667 
       
   668             int    nbits = 32;
       
   669             int    bpl_bmp = ((image.width()*nbits+31)/32)*4;
       
   670 
       
   671             entries[i].bColorCount = 0;
       
   672             entries[i].bReserved = 0;
       
   673             entries[i].wBitCount = nbits;
       
   674             entries[i].bHeight = image.height();
       
   675             entries[i].bWidth = image.width();
       
   676             entries[i].dwBytesInRes = BMP_INFOHDR_SIZE + (bpl_bmp * image.height())
       
   677                 + (maskImage.bytesPerLine() * maskImage.height());
       
   678             entries[i].wPlanes = 1;
       
   679             if (i == 0)
       
   680                 entries[i].dwImageOffset = origOffset + ICONDIR_SIZE
       
   681                 + (id.idCount * ICONDIRENTRY_SIZE);
       
   682             else
       
   683                 entries[i].dwImageOffset = entries[i-1].dwImageOffset + entries[i-1].dwBytesInRes;
       
   684 
       
   685             bmpHeaders[i].biBitCount = entries[i].wBitCount;
       
   686             bmpHeaders[i].biClrImportant = 0;
       
   687             bmpHeaders[i].biClrUsed = entries[i].bColorCount;
       
   688             bmpHeaders[i].biCompression = 0;
       
   689             bmpHeaders[i].biHeight = entries[i].bHeight * 2; // 2 is for the mask
       
   690             bmpHeaders[i].biPlanes = entries[i].wPlanes;
       
   691             bmpHeaders[i].biSize = BMP_INFOHDR_SIZE;
       
   692             bmpHeaders[i].biSizeImage = entries[i].dwBytesInRes - BMP_INFOHDR_SIZE;
       
   693             bmpHeaders[i].biWidth = entries[i].bWidth;
       
   694             bmpHeaders[i].biXPelsPerMeter = 0;
       
   695             bmpHeaders[i].biYPelsPerMeter = 0;
       
   696 
       
   697             QBuffer buffer(&imageData[i]);
       
   698             buffer.open(QIODevice::WriteOnly);
       
   699 
       
   700             uchar *buf = new uchar[bpl_bmp];
       
   701             uchar *b;
       
   702             memset( buf, 0, bpl_bmp );
       
   703             int y;
       
   704             for (y = image.height() - 1; y >= 0; y--) {    // write the image bits
       
   705                 // 32 bits
       
   706                 QRgb *p   = (QRgb *)image.scanLine(y);
       
   707                 QRgb *end = p + image.width();
       
   708                 b = buf;
       
   709                 int x = 0;
       
   710                 while (p < end) {
       
   711                     *b++ = qBlue(*p);
       
   712                     *b++ = qGreen(*p);
       
   713                     *b++ = qRed(*p);
       
   714                     *b++ = qAlpha(*p);
       
   715                     if (qAlpha(*p) > 0)   // Even mostly transparent pixels must not be masked away
       
   716                         maskImage.setPixel(x, y, Qt::color1);  // (i.e. createAlphaMask() takes away too much)
       
   717                     p++;
       
   718                     x++;
       
   719                 }
       
   720                 buffer.write((char*)buf, bpl_bmp);
       
   721             }
       
   722             delete[] buf;
       
   723 
       
   724             maskImage.invertPixels();   // seems as though it needs this
       
   725             // NOTE! !! The mask is only flipped vertically - not horizontally !!
       
   726             for (y = maskImage.height() - 1; y >= 0; y--)
       
   727                 buffer.write((char*)maskImage.scanLine(y), maskImage.bytesPerLine());
       
   728         }
       
   729 
       
   730         if (writeIconDir(device, id)) {
       
   731             int i;
       
   732             bool bOK = true;
       
   733             for (i = 0; i < id.idCount && bOK; i++) {
       
   734                 bOK = writeIconDirEntry(device, entries[i]);
       
   735             }
       
   736             if (bOK) {
       
   737                 for (i = 0; i < id.idCount && bOK; i++) {
       
   738                     bOK = writeBMPInfoHeader(device, bmpHeaders[i]);
       
   739                     bOK &= (device->write(imageData[i]) == (int) imageData[i].size());
       
   740                 }
       
   741                 retValue = bOK;
       
   742             }
       
   743         }
       
   744 
       
   745         delete [] entries;
       
   746         delete [] bmpHeaders;
       
   747         delete [] imageData;
       
   748 
       
   749     }
       
   750     return retValue;
       
   751 }
       
   752 
       
   753 /*!
       
   754     Constructs an instance of QtIcoHandler initialized to use \a device.
       
   755 */
       
   756 QtIcoHandler::QtIcoHandler(QIODevice *device)
       
   757 {
       
   758     m_currentIconIndex = 0;
       
   759     setDevice(device);
       
   760     m_pICOReader = new ICOReader(device);
       
   761 }
       
   762 
       
   763 /*!
       
   764     Destructor for QtIcoHandler.
       
   765 */
       
   766 QtIcoHandler::~QtIcoHandler()
       
   767 {
       
   768     delete m_pICOReader;
       
   769 }
       
   770 
       
   771 /*!
       
   772  * Verifies if some values (magic bytes) are set as expected in the header of the file.
       
   773  * If the magic bytes were found, it is assumed that the QtIcoHandler can read the file.
       
   774  *
       
   775  */
       
   776 bool QtIcoHandler::canRead() const
       
   777 {
       
   778     bool bCanRead = false;
       
   779     QIODevice *device = QImageIOHandler::device();
       
   780     if (device) {
       
   781         bCanRead = ICOReader::canRead(device);
       
   782         if (bCanRead)
       
   783             setFormat("ico");
       
   784     } else {
       
   785         qWarning("QtIcoHandler::canRead() called with no device");
       
   786     }
       
   787     return bCanRead;
       
   788 }
       
   789 
       
   790 /*! This static function is used by the plugin code, and is provided for convenience only.
       
   791     \a device must be an opened device with pointing to the start of the header data of the ICO file.
       
   792 */
       
   793 bool QtIcoHandler::canRead(QIODevice *device)
       
   794 {
       
   795     Q_ASSERT(device);
       
   796     return ICOReader::canRead(device);
       
   797 }
       
   798 
       
   799 /*! \reimp
       
   800 
       
   801 */
       
   802 bool QtIcoHandler::read(QImage *image)
       
   803 {
       
   804     bool bSuccess = false;
       
   805     QImage img = m_pICOReader->iconAt(m_currentIconIndex);
       
   806 
       
   807     // Make sure we only write to \a image when we succeed.
       
   808     if (!img.isNull()) {
       
   809         bSuccess = true;
       
   810         *image = img;
       
   811     }
       
   812 
       
   813     return bSuccess;
       
   814 }
       
   815 
       
   816 
       
   817 /*! \reimp
       
   818 
       
   819 */
       
   820 bool QtIcoHandler::write(const QImage &image)
       
   821 {
       
   822     QIODevice *device = QImageIOHandler::device();
       
   823     QList<QImage> imgs;
       
   824     imgs.append(image);
       
   825     return ICOReader::write(device, imgs);
       
   826 }
       
   827 
       
   828 /*!
       
   829  * Return the common identifier of the format.
       
   830  * For ICO format this will return "ico".
       
   831  */
       
   832 QByteArray QtIcoHandler::name() const
       
   833 {
       
   834     return "ico";
       
   835 }
       
   836 
       
   837 
       
   838 /*! \reimp
       
   839 
       
   840 */
       
   841 int QtIcoHandler::imageCount() const
       
   842 {
       
   843     return m_pICOReader->count();
       
   844 }
       
   845 
       
   846 /*! \reimp
       
   847 
       
   848 */
       
   849 bool QtIcoHandler::jumpToImage(int imageNumber)
       
   850 {
       
   851     if (imageNumber < imageCount()) {
       
   852         m_currentIconIndex = imageNumber;
       
   853     }
       
   854 
       
   855     return (imageNumber < imageCount()) ? true : false;
       
   856 }
       
   857 
       
   858 /*! \reimp
       
   859 
       
   860 */
       
   861 bool QtIcoHandler::jumpToNextImage()
       
   862 {
       
   863     return jumpToImage(m_currentIconIndex + 1);
       
   864 }
       
   865