webengine/osswebengine/WebKit/qt/Plugins/ICOHandler.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * kimgio import filter for MS Windows .ico files
       
     3  *
       
     4  * Distributed under the terms of the LGPL
       
     5  * Copyright (c) 2000 Malte Starostik <malte@kde.org>
       
     6  *
       
     7  */
       
     8 
       
     9 #include "ICOHandler.h"
       
    10 
       
    11 #include <cstring>
       
    12 #include <cstdlib>
       
    13 #include <algorithm>
       
    14 #include <vector>
       
    15 
       
    16 #include <QtGui/QImage>
       
    17 #include <QtGui/QBitmap>
       
    18 #include <QtGui/QApplication>
       
    19 #include <QtCore/QVector>
       
    20 #include <QtGui/QDesktopWidget>
       
    21 
       
    22 namespace
       
    23 {
       
    24     // Global header (see http://www.daubnet.com/formats/ICO.html)
       
    25     struct IcoHeader
       
    26     {
       
    27         enum Type { Icon = 1, Cursor };
       
    28         quint16 reserved;
       
    29         quint16 type;
       
    30         quint16 count;
       
    31     };
       
    32 
       
    33     inline QDataStream& operator >>( QDataStream& s, IcoHeader& h )
       
    34     {
       
    35         return s >> h.reserved >> h.type >> h.count;
       
    36     }
       
    37 
       
    38     // Based on qt_read_dib et al. from qimage.cpp
       
    39     // (c) 1992-2002 Trolltech AS.
       
    40     struct BMP_INFOHDR
       
    41     {
       
    42         static const quint32 Size = 40;
       
    43         quint32  biSize;                // size of this struct
       
    44         quint32  biWidth;               // pixmap width
       
    45         quint32  biHeight;              // pixmap height
       
    46         quint16  biPlanes;              // should be 1
       
    47         quint16  biBitCount;            // number of bits per pixel
       
    48         enum Compression { RGB = 0 };
       
    49         quint32  biCompression;         // compression method
       
    50         quint32  biSizeImage;           // size of image
       
    51         quint32  biXPelsPerMeter;       // horizontal resolution
       
    52         quint32  biYPelsPerMeter;       // vertical resolution
       
    53         quint32  biClrUsed;             // number of colors used
       
    54         quint32  biClrImportant;        // number of important colors
       
    55     };
       
    56     const quint32 BMP_INFOHDR::Size;
       
    57 
       
    58     QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi )
       
    59     {
       
    60         s >> bi.biSize;
       
    61         if ( bi.biSize == BMP_INFOHDR::Size )
       
    62         {
       
    63             s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
       
    64             s >> bi.biCompression >> bi.biSizeImage;
       
    65             s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
       
    66             s >> bi.biClrUsed >> bi.biClrImportant;
       
    67         }
       
    68         return s;
       
    69     }
       
    70 
       
    71 #if 0
       
    72     QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi )
       
    73     {
       
    74         s << bi.biSize;
       
    75         s << bi.biWidth << bi.biHeight;
       
    76         s << bi.biPlanes;
       
    77         s << bi.biBitCount;
       
    78         s << bi.biCompression;
       
    79         s << bi.biSizeImage;
       
    80         s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
       
    81         s << bi.biClrUsed << bi.biClrImportant;
       
    82         return s;
       
    83     }
       
    84 #endif
       
    85 
       
    86     // Header for every icon in the file
       
    87     struct IconRec
       
    88     {
       
    89         unsigned char width;
       
    90         unsigned char height;
       
    91         quint16 colors;
       
    92         quint16 hotspotX;
       
    93         quint16 hotspotY;
       
    94         quint32 size;
       
    95         quint32 offset;
       
    96     };
       
    97 
       
    98     inline QDataStream& operator >>( QDataStream& s, IconRec& r )
       
    99     {
       
   100         return s >> r.width >> r.height >> r.colors
       
   101                  >> r.hotspotX >> r.hotspotY >> r.size >> r.offset;
       
   102     }
       
   103 
       
   104     struct LessDifference
       
   105     {
       
   106         LessDifference( unsigned s, unsigned c )
       
   107             : size( s ), colors( c ) {}
       
   108 
       
   109         bool operator ()( const IconRec& lhs, const IconRec& rhs ) const
       
   110         {
       
   111             // closest size match precedes everything else
       
   112             if ( std::abs( int( lhs.width - size ) ) <
       
   113                  std::abs( int( rhs.width - size ) ) ) return true;
       
   114             else if ( std::abs( int( lhs.width - size ) ) >
       
   115                  std::abs( int( rhs.width - size ) ) ) return false;
       
   116             else if ( colors == 0 )
       
   117             {
       
   118                 // high/true color requested
       
   119                 if ( lhs.colors == 0 ) return true;
       
   120                 else if ( rhs.colors == 0 ) return false;
       
   121                 else return lhs.colors > rhs.colors;
       
   122             }
       
   123             else
       
   124             {
       
   125                 // indexed icon requested
       
   126                 if ( lhs.colors == 0 && rhs.colors == 0 ) return false;
       
   127                 else if ( lhs.colors == 0 ) return false;
       
   128                 else return std::abs( int( lhs.colors - colors ) ) <
       
   129                             std::abs( int( rhs.colors - colors ) );
       
   130             }
       
   131         }
       
   132         unsigned size;
       
   133         unsigned colors;
       
   134     };
       
   135 
       
   136     bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon )
       
   137     {
       
   138         BMP_INFOHDR header;
       
   139         stream >> header;
       
   140         if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size ||
       
   141              header.biSize > rec.size ||
       
   142              header.biCompression != BMP_INFOHDR::RGB ||
       
   143              ( header.biBitCount != 1 && header.biBitCount != 4 &&
       
   144                header.biBitCount != 8 && header.biBitCount != 24 &&
       
   145                header.biBitCount != 32 ) ) return false;
       
   146 
       
   147         unsigned paletteSize, paletteEntries;
       
   148 
       
   149         if (header.biBitCount > 8)
       
   150         {
       
   151             paletteEntries = 0;
       
   152             paletteSize    = 0;
       
   153         }
       
   154         else
       
   155         {
       
   156             paletteSize    = (1 << header.biBitCount);
       
   157             paletteEntries = paletteSize;
       
   158             if (header.biClrUsed && header.biClrUsed < paletteSize)
       
   159                 paletteEntries = header.biClrUsed;
       
   160         }
       
   161 
       
   162         // Always create a 32-bit image to get the mask right
       
   163         // Note: this is safe as rec.width, rec.height are bytes
       
   164         icon = QImage( rec.width, rec.height, QImage::Format_ARGB32 );
       
   165         if ( icon.isNull() ) return false;
       
   166 
       
   167         QVector< QRgb > colorTable( paletteSize );
       
   168 
       
   169         colorTable.fill( QRgb( 0 ) );
       
   170         for ( unsigned i = 0; i < paletteEntries; ++i )
       
   171         {
       
   172             unsigned char rgb[ 4 ];
       
   173             stream.readRawData( reinterpret_cast< char* >( &rgb ),
       
   174                                  sizeof( rgb ) );
       
   175             colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] );
       
   176         }
       
   177 
       
   178         unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4;
       
   179 
       
   180         unsigned char* buf = new unsigned char[ bpl ];
       
   181         for ( unsigned y = rec.height; !stream.atEnd() && y--; )
       
   182         {
       
   183             stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
       
   184             unsigned char* pixel = buf;
       
   185             QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine( y ) );
       
   186             switch ( header.biBitCount )
       
   187             {
       
   188                 case 1:
       
   189                     for ( unsigned x = 0; x < rec.width; ++x )
       
   190                         *p++ = colorTable[
       
   191                             ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ];
       
   192                     break;
       
   193                 case 4:
       
   194                     for ( unsigned x = 0; x < rec.width; ++x )
       
   195                         if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ];
       
   196                         else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ];
       
   197                     break;
       
   198                 case 8:
       
   199                     for ( unsigned x = 0; x < rec.width; ++x )
       
   200                         *p++ = colorTable[ pixel[ x ] ];
       
   201                     break;
       
   202                 case 24:
       
   203                     for ( unsigned x = 0; x < rec.width; ++x )
       
   204                         *p++ = qRgb( pixel[ 3 * x + 2 ],
       
   205                                      pixel[ 3 * x + 1 ],
       
   206                                      pixel[ 3 * x ] );
       
   207                     break;
       
   208                 case 32:
       
   209                     for ( unsigned x = 0; x < rec.width; ++x )
       
   210                         *p++ = qRgba( pixel[ 4 * x + 2 ],
       
   211                                       pixel[ 4 * x + 1 ],
       
   212                                       pixel[ 4 * x ],
       
   213                                       pixel[ 4 * x  + 3] );
       
   214                     break;
       
   215             }
       
   216         }
       
   217         delete[] buf;
       
   218 
       
   219         if ( header.biBitCount < 32 )
       
   220         {
       
   221             // Traditional 1-bit mask
       
   222             bpl = ( rec.width + 31 ) / 32 * 4;
       
   223             buf = new unsigned char[ bpl ];
       
   224             for ( unsigned y = rec.height; y--; )
       
   225             {
       
   226                 stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
       
   227                 QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine( y ) );
       
   228                 for ( unsigned x = 0; x < rec.width; ++x, ++p )
       
   229                     if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) )
       
   230                         *p &= RGB_MASK;
       
   231             }
       
   232             delete[] buf;
       
   233         }
       
   234         return true;
       
   235     }
       
   236 }
       
   237 
       
   238 ICOHandler::ICOHandler()
       
   239 {
       
   240 }
       
   241 
       
   242 bool ICOHandler::canRead() const
       
   243 {
       
   244     return canRead(device());
       
   245 }
       
   246 
       
   247 bool ICOHandler::read(QImage *outImage)
       
   248 {
       
   249 
       
   250     qint64 offset = device()->pos();
       
   251 
       
   252     QDataStream stream( device() );
       
   253     stream.setByteOrder( QDataStream::LittleEndian );
       
   254     IcoHeader header;
       
   255     stream >> header;
       
   256     if ( stream.atEnd() || !header.count ||
       
   257          ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) )
       
   258         return false;
       
   259 
       
   260     unsigned requestedSize = 32;
       
   261     unsigned requestedColors =  QApplication::desktop()->depth() > 8 ? 0 : QApplication::desktop()->depth();
       
   262     int requestedIndex = -1;
       
   263 #if 0
       
   264     if ( io->parameters() )
       
   265     {
       
   266         QStringList params = QString(io->parameters()).split( ';', QString::SkipEmptyParts );
       
   267         QMap< QString, QString > options;
       
   268         for ( QStringList::ConstIterator it = params.begin();
       
   269               it != params.end(); ++it )
       
   270         {
       
   271             QStringList tmp = (*it).split( '=', QString::SkipEmptyParts );
       
   272             if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ];
       
   273         }
       
   274         if ( options[ "index" ].toUInt() )
       
   275             requestedIndex = options[ "index" ].toUInt();
       
   276         if ( options[ "size" ].toUInt() )
       
   277             requestedSize = options[ "size" ].toUInt();
       
   278         if ( options[ "colors" ].toUInt() )
       
   279             requestedColors = options[ "colors" ].toUInt();
       
   280     }
       
   281 #endif
       
   282 
       
   283     typedef std::vector< IconRec > IconList;
       
   284     IconList icons;
       
   285     for ( unsigned i = 0; i < header.count; ++i )
       
   286     {
       
   287         if ( stream.atEnd() )
       
   288             return false;
       
   289         IconRec rec;
       
   290         stream >> rec;
       
   291         icons.push_back( rec );
       
   292     }
       
   293     IconList::const_iterator selected;
       
   294     if (requestedIndex >= 0) {
       
   295         selected = std::min( icons.begin() + requestedIndex, icons.end() );
       
   296     } else {
       
   297         selected = std::min_element( icons.begin(), icons.end(),
       
   298                                      LessDifference( requestedSize, requestedColors ) );
       
   299     }
       
   300     if ( stream.atEnd() || selected == icons.end() ||
       
   301          offset + selected->offset > device()->size() )
       
   302         return false;
       
   303 
       
   304     device()->seek( offset + selected->offset );
       
   305     QImage icon;
       
   306     if ( loadFromDIB( stream, *selected, icon ) )
       
   307     {
       
   308 #ifndef QT_NO_IMAGE_TEXT
       
   309         icon.setText( "X-Index", 0, QString::number( selected - icons.begin() ) );
       
   310         if ( header.type == IcoHeader::Cursor )
       
   311         {
       
   312             icon.setText( "X-HotspotX", 0, QString::number( selected->hotspotX ) );
       
   313             icon.setText( "X-HotspotY", 0, QString::number( selected->hotspotY ) );
       
   314         }
       
   315 #endif
       
   316         *outImage = icon;
       
   317         return true;
       
   318     }
       
   319     return false;
       
   320 }
       
   321 
       
   322 bool ICOHandler::write(const QImage &/*image*/)
       
   323 {
       
   324 #if 0
       
   325     if (image.isNull())
       
   326         return;
       
   327 
       
   328     QByteArray dibData;
       
   329     QDataStream dib(dibData, QIODevice::ReadWrite);
       
   330     dib.setByteOrder(QDataStream::LittleEndian);
       
   331 
       
   332     QImage pixels = image;
       
   333     QImage mask;
       
   334     if (io->image().hasAlphaBuffer())
       
   335         mask = image.createAlphaMask();
       
   336     else
       
   337         mask = image.createHeuristicMask();
       
   338     mask.invertPixels();
       
   339     for ( int y = 0; y < pixels.height(); ++y )
       
   340         for ( int x = 0; x < pixels.width(); ++x )
       
   341             if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 );
       
   342 
       
   343     if (!qt_write_dib(dib, pixels))
       
   344         return;
       
   345 
       
   346    uint hdrPos = dib.device()->at();
       
   347     if (!qt_write_dib(dib, mask))
       
   348         return;
       
   349     memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8);
       
   350     dibData.resize(dibData.size() - BMP_WIN - 8);
       
   351 
       
   352     QDataStream ico(device());
       
   353     ico.setByteOrder(QDataStream::LittleEndian);
       
   354     IcoHeader hdr;
       
   355     hdr.reserved = 0;
       
   356     hdr.type = Icon;
       
   357     hdr.count = 1;
       
   358     ico << hdr.reserved << hdr.type << hdr.count;
       
   359     IconRec rec;
       
   360     rec.width = image.width();
       
   361     rec.height = image.height();
       
   362     if (image.numColors() <= 16)
       
   363         rec.colors = 16;
       
   364     else if (image.depth() <= 8)
       
   365         rec.colors = 256;
       
   366     else
       
   367         rec.colors = 0;
       
   368     rec.hotspotX = 0;
       
   369     rec.hotspotY = 0;
       
   370     rec.dibSize = dibData.size();
       
   371     ico << rec.width << rec.height << rec.colors
       
   372         << rec.hotspotX << rec.hotspotY << rec.dibSize;
       
   373     rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset);
       
   374     ico << rec.dibOffset;
       
   375 
       
   376     BMP_INFOHDR dibHeader;
       
   377     dib.device()->at(0);
       
   378     dib >> dibHeader;
       
   379     dibHeader.biHeight = image.height() << 1;
       
   380     dib.device()->at(0);
       
   381     dib << dibHeader;
       
   382 
       
   383     ico.writeRawBytes(dibData.data(), dibData.size());
       
   384     return true;
       
   385 #endif
       
   386     return false;
       
   387 }
       
   388 
       
   389 QByteArray ICOHandler::name() const
       
   390 {
       
   391     return "ico";
       
   392 }
       
   393 
       
   394 bool ICOHandler::canRead(QIODevice *device)
       
   395 {
       
   396     if (!device) {
       
   397         qWarning("ICOHandler::canRead() called with no device");
       
   398         return false;
       
   399     }
       
   400 
       
   401     const qint64 oldPos = device->pos();
       
   402 
       
   403     char head[8];
       
   404     qint64 readBytes = device->read(head, sizeof(head));
       
   405     const bool readOk = readBytes == sizeof(head);
       
   406 
       
   407     if (device->isSequential()) {
       
   408         while (readBytes > 0)
       
   409             device->ungetChar(head[readBytes-- - 1]);
       
   410     } else {
       
   411         device->seek(oldPos);
       
   412     }
       
   413 
       
   414     if ( !readOk )
       
   415         return false;
       
   416 
       
   417     return head[2] == '\001' && head[3] == '\000' && // type should be 1
       
   418         ( head[6] == 16 || head[6] == 32 || head[6] == 64 ) && // width can only be one of those
       
   419         ( head[7] == 16 || head[7] == 32 || head[7] == 64 );   // same for height
       
   420 }
       
   421 
       
   422 class ICOPlugin : public QImageIOPlugin
       
   423 {
       
   424 public:
       
   425     QStringList keys() const;
       
   426     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
       
   427     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
       
   428 };
       
   429 
       
   430 QStringList ICOPlugin::keys() const
       
   431 {
       
   432     return QStringList() << "ico" << "ICO";
       
   433 }
       
   434 
       
   435 QImageIOPlugin::Capabilities ICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
       
   436 {
       
   437     if (format == "ico" || format == "ICO")
       
   438         return Capabilities(CanRead);
       
   439     if (!format.isEmpty())
       
   440         return 0;
       
   441     if (!device->isOpen())
       
   442         return 0;
       
   443 
       
   444     Capabilities cap;
       
   445     if (device->isReadable() && ICOHandler::canRead(device))
       
   446         cap |= CanRead;
       
   447     return cap;
       
   448 }
       
   449 
       
   450 QImageIOHandler *ICOPlugin::create(QIODevice *device, const QByteArray &format) const
       
   451 {
       
   452     QImageIOHandler *handler = new ICOHandler;
       
   453     handler->setDevice(device);
       
   454     handler->setFormat(format);
       
   455     return handler;
       
   456 }
       
   457 
       
   458 Q_EXPORT_STATIC_PLUGIN(ICOPlugin)
       
   459 Q_EXPORT_PLUGIN2(qtwebico, ICOPlugin)