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