• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KImgIO

ico.cpp

Go to the documentation of this file.
00001 /*
00002  * kimgio import filter for MS Windows .ico files
00003  *
00004  * Distributed under the terms of the LGPL
00005  * Copyright (c) 2000 Malte Starostik <malte@kde.org>
00006  *
00007  */
00008 
00009 // remove when QImage::jumpTable is ported
00010 #define QT3_SUPPORT
00011 #define QT3_SUPPORT_WARNINGS
00012 #ifdef __GNUC__
00013 #warning TODO: remove QT3_SUPPORT
00014 #endif
00015 
00016 #include "ico.h"
00017 
00018 #include <cstring>
00019 #include <cstdlib>
00020 #include <algorithm>
00021 #include <vector>
00022 
00023 #include <QtGui/QImage>
00024 #include <QtGui/QBitmap>
00025 #include <QtGui/QApplication>
00026 #include <QtCore/QVector>
00027 #include <QtGui/QDesktopWidget>
00028 
00029 namespace
00030 {
00031     // Global header (see http://www.daubnet.com/formats/ICO.html)
00032     struct IcoHeader
00033     {
00034         enum Type { Icon = 1, Cursor };
00035         quint16 reserved;
00036         quint16 type;
00037         quint16 count;
00038     };
00039 
00040     inline QDataStream& operator >>( QDataStream& s, IcoHeader& h )
00041     {
00042         return s >> h.reserved >> h.type >> h.count;
00043     }
00044 
00045     // Based on qt_read_dib et al. from qimage.cpp
00046     // (c) 1992-2002 Trolltech AS.
00047     struct BMP_INFOHDR
00048     {
00049         static const quint32 Size = 40;
00050         quint32  biSize;                // size of this struct
00051         quint32  biWidth;               // pixmap width
00052         quint32  biHeight;              // pixmap height
00053         quint16  biPlanes;              // should be 1
00054         quint16  biBitCount;            // number of bits per pixel
00055         enum Compression { RGB = 0 };
00056         quint32  biCompression;         // compression method
00057         quint32  biSizeImage;           // size of image
00058         quint32  biXPelsPerMeter;       // horizontal resolution
00059         quint32  biYPelsPerMeter;       // vertical resolution
00060         quint32  biClrUsed;             // number of colors used
00061         quint32  biClrImportant;        // number of important colors
00062     };
00063     const quint32 BMP_INFOHDR::Size;
00064 
00065     QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi )
00066     {
00067         s >> bi.biSize;
00068         if ( bi.biSize == BMP_INFOHDR::Size )
00069         {
00070             s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
00071             s >> bi.biCompression >> bi.biSizeImage;
00072             s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
00073             s >> bi.biClrUsed >> bi.biClrImportant;
00074         }
00075         return s;
00076     }
00077 
00078 #if 0
00079     QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi )
00080     {
00081         s << bi.biSize;
00082         s << bi.biWidth << bi.biHeight;
00083         s << bi.biPlanes;
00084         s << bi.biBitCount;
00085         s << bi.biCompression;
00086         s << bi.biSizeImage;
00087         s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
00088         s << bi.biClrUsed << bi.biClrImportant;
00089         return s;
00090     }
00091 #endif
00092 
00093     // Header for every icon in the file
00094     struct IconRec
00095     {
00096         unsigned char width;
00097         unsigned char height;
00098         quint16 colors;
00099         quint16 hotspotX;
00100         quint16 hotspotY;
00101         quint32 size;
00102         quint32 offset;
00103     };
00104 
00105     inline QDataStream& operator >>( QDataStream& s, IconRec& r )
00106     {
00107         return s >> r.width >> r.height >> r.colors
00108                  >> r.hotspotX >> r.hotspotY >> r.size >> r.offset;
00109     }
00110 
00111     struct LessDifference
00112     {
00113         LessDifference( unsigned s, unsigned c )
00114             : size( s ), colors( c ) {}
00115 
00116         bool operator ()( const IconRec& lhs, const IconRec& rhs ) const
00117         {
00118             // closest size match precedes everything else
00119             if ( std::abs( int( lhs.width - size ) ) <
00120                  std::abs( int( rhs.width - size ) ) ) return true;
00121             else if ( std::abs( int( lhs.width - size ) ) >
00122                  std::abs( int( rhs.width - size ) ) ) return false;
00123             else if ( colors == 0 )
00124             {
00125                 // high/true color requested
00126                 if ( lhs.colors == 0 ) return true;
00127                 else if ( rhs.colors == 0 ) return false;
00128                 else return lhs.colors > rhs.colors;
00129             }
00130             else
00131             {
00132                 // indexed icon requested
00133                 if ( lhs.colors == 0 && rhs.colors == 0 ) return false;
00134                 else if ( lhs.colors == 0 ) return false;
00135                 else return std::abs( int( lhs.colors - colors ) ) <
00136                             std::abs( int( rhs.colors - colors ) );
00137             }
00138         }
00139         unsigned size;
00140         unsigned colors;
00141     };
00142 
00143     bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon )
00144     {
00145         BMP_INFOHDR header;
00146         stream >> header;
00147         if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size ||
00148              header.biSize > rec.size ||
00149              header.biCompression != BMP_INFOHDR::RGB ||
00150              ( header.biBitCount != 1 && header.biBitCount != 4 &&
00151                header.biBitCount != 8 && header.biBitCount != 24 &&
00152                header.biBitCount != 32 ) ) return false;
00153 
00154         unsigned paletteSize, paletteEntries;
00155 
00156         if (header.biBitCount > 8)
00157         {
00158             paletteEntries = 0;
00159             paletteSize    = 0;
00160         }
00161         else
00162         {
00163             paletteSize    = (1 << header.biBitCount);
00164             paletteEntries = paletteSize;
00165             if (header.biClrUsed && header.biClrUsed < paletteSize)
00166                 paletteEntries = header.biClrUsed;
00167         }
00168 
00169         // Always create a 32-bit image to get the mask right
00170         // Note: this is safe as rec.width, rec.height are bytes
00171         icon = QImage( rec.width, rec.height, QImage::Format_ARGB32 );
00172         if ( icon.isNull() ) return false;
00173 
00174         QVector< QRgb > colorTable( paletteSize );
00175 
00176         colorTable.fill( QRgb( 0 ) );
00177         for ( unsigned i = 0; i < paletteEntries; ++i )
00178         {
00179             unsigned char rgb[ 4 ];
00180             stream.readRawData( reinterpret_cast< char* >( &rgb ),
00181                                  sizeof( rgb ) );
00182             colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] );
00183         }
00184 
00185         unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4;
00186 
00187         unsigned char* buf = new unsigned char[ bpl ];
00188         unsigned char** lines = icon.jumpTable();
00189         for ( unsigned y = rec.height; !stream.atEnd() && y--; )
00190         {
00191             stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
00192             unsigned char* pixel = buf;
00193             QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
00194             switch ( header.biBitCount )
00195             {
00196                 case 1:
00197                     for ( unsigned x = 0; x < rec.width; ++x )
00198                         *p++ = colorTable[
00199                             ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ];
00200                     break;
00201                 case 4:
00202                     for ( unsigned x = 0; x < rec.width; ++x )
00203                         if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ];
00204                         else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ];
00205                     break;
00206                 case 8:
00207                     for ( unsigned x = 0; x < rec.width; ++x )
00208                         *p++ = colorTable[ pixel[ x ] ];
00209                     break;
00210                 case 24:
00211                     for ( unsigned x = 0; x < rec.width; ++x )
00212                         *p++ = qRgb( pixel[ 3 * x + 2 ],
00213                                      pixel[ 3 * x + 1 ],
00214                                      pixel[ 3 * x ] );
00215                     break;
00216                 case 32:
00217                     for ( unsigned x = 0; x < rec.width; ++x )
00218                         *p++ = qRgba( pixel[ 4 * x + 2 ],
00219                                       pixel[ 4 * x + 1 ],
00220                                       pixel[ 4 * x ],
00221                                       pixel[ 4 * x  + 3] );
00222                     break;
00223             }
00224         }
00225         delete[] buf;
00226 
00227         if ( header.biBitCount < 32 )
00228         {
00229             // Traditional 1-bit mask
00230             bpl = ( rec.width + 31 ) / 32 * 4;
00231             buf = new unsigned char[ bpl ];
00232             for ( unsigned y = rec.height; y--; )
00233             {
00234                 stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
00235                 QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
00236                 for ( unsigned x = 0; x < rec.width; ++x, ++p )
00237                     if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) )
00238                         *p &= RGB_MASK;
00239             }
00240             delete[] buf;
00241         }
00242         return true;
00243     }
00244 }
00245 
00246 ICOHandler::ICOHandler()
00247 {
00248 }
00249 
00250 bool ICOHandler::canRead() const
00251 {
00252     if (canRead(device())) {
00253         setFormat("ico");
00254         return true;
00255     }
00256     return false;
00257 }
00258 
00259 bool ICOHandler::read(QImage *outImage)
00260 {
00261 
00262     qint64 offset = device()->pos();
00263 
00264     QDataStream stream( device() );
00265     stream.setByteOrder( QDataStream::LittleEndian );
00266     IcoHeader header;
00267     stream >> header;
00268     if ( stream.atEnd() || !header.count ||
00269          ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) )
00270         return false;
00271 
00272     unsigned requestedSize = 32;
00273     unsigned requestedColors =  QApplication::desktop()->depth() > 8 ? 0 : QApplication::desktop()->depth();
00274     int requestedIndex = -1;
00275 #if 0
00276     if ( io->parameters() )
00277     {
00278         QStringList params = QString(io->parameters()).split( ';', QString::SkipEmptyParts );
00279         QMap< QString, QString > options;
00280         for ( QStringList::ConstIterator it = params.begin();
00281               it != params.end(); ++it )
00282         {
00283             QStringList tmp = (*it).split( '=', QString::SkipEmptyParts );
00284             if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ];
00285         }
00286         if ( options[ "index" ].toUInt() )
00287             requestedIndex = options[ "index" ].toUInt();
00288         if ( options[ "size" ].toUInt() )
00289             requestedSize = options[ "size" ].toUInt();
00290         if ( options[ "colors" ].toUInt() )
00291             requestedColors = options[ "colors" ].toUInt();
00292     }
00293 #endif
00294 
00295     typedef std::vector< IconRec > IconList;
00296     IconList icons;
00297     for ( unsigned i = 0; i < header.count; ++i )
00298     {
00299         if ( stream.atEnd() )
00300             return false;
00301         IconRec rec;
00302         stream >> rec;
00303         icons.push_back( rec );
00304     }
00305     IconList::const_iterator selected;
00306     if (requestedIndex >= 0) {
00307         selected = std::min( icons.begin() + requestedIndex, icons.end() );
00308     } else {
00309         selected = std::min_element( icons.begin(), icons.end(),
00310                                      LessDifference( requestedSize, requestedColors ) );
00311     }
00312     if ( stream.atEnd() || selected == icons.end() ||
00313          offset + selected->offset > device()->size() )
00314         return false;
00315 
00316     device()->seek( offset + selected->offset );
00317     QImage icon;
00318     if ( loadFromDIB( stream, *selected, icon ) )
00319     {
00320         icon.setText( "X-Index", 0, QString::number( selected - icons.begin() ) );
00321         if ( header.type == IcoHeader::Cursor )
00322         {
00323             icon.setText( "X-HotspotX", 0, QString::number( selected->hotspotX ) );
00324             icon.setText( "X-HotspotY", 0, QString::number( selected->hotspotY ) );
00325         }
00326 
00327         *outImage = icon;
00328         return true;
00329     }
00330     return false;
00331 }
00332 
00333 bool ICOHandler::write(const QImage &/*image*/)
00334 {
00335 #if 0
00336     if (image.isNull())
00337         return;
00338 
00339     QByteArray dibData;
00340     QDataStream dib(dibData, QIODevice::ReadWrite);
00341     dib.setByteOrder(QDataStream::LittleEndian);
00342 
00343     QImage pixels = image;
00344     QImage mask;
00345     if (io->image().hasAlphaBuffer())
00346         mask = image.createAlphaMask();
00347     else
00348         mask = image.createHeuristicMask();
00349     mask.invertPixels();
00350     for ( int y = 0; y < pixels.height(); ++y )
00351         for ( int x = 0; x < pixels.width(); ++x )
00352             if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 );
00353 
00354     if (!qt_write_dib(dib, pixels))
00355         return;
00356 
00357    uint hdrPos = dib.device()->at();
00358     if (!qt_write_dib(dib, mask))
00359         return;
00360     memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8);
00361     dibData.resize(dibData.size() - BMP_WIN - 8);
00362 
00363     QDataStream ico(device());
00364     ico.setByteOrder(QDataStream::LittleEndian);
00365     IcoHeader hdr;
00366     hdr.reserved = 0;
00367     hdr.type = Icon;
00368     hdr.count = 1;
00369     ico << hdr.reserved << hdr.type << hdr.count;
00370     IconRec rec;
00371     rec.width = image.width();
00372     rec.height = image.height();
00373     if (image.numColors() <= 16)
00374         rec.colors = 16;
00375     else if (image.depth() <= 8)
00376         rec.colors = 256;
00377     else
00378         rec.colors = 0;
00379     rec.hotspotX = 0;
00380     rec.hotspotY = 0;
00381     rec.dibSize = dibData.size();
00382     ico << rec.width << rec.height << rec.colors
00383         << rec.hotspotX << rec.hotspotY << rec.dibSize;
00384     rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset);
00385     ico << rec.dibOffset;
00386 
00387     BMP_INFOHDR dibHeader;
00388     dib.device()->at(0);
00389     dib >> dibHeader;
00390     dibHeader.biHeight = image.height() << 1;
00391     dib.device()->at(0);
00392     dib << dibHeader;
00393 
00394     ico.writeRawBytes(dibData.data(), dibData.size());
00395     return true;
00396 #endif
00397     return false;
00398 }
00399 
00400 QByteArray ICOHandler::name() const
00401 {
00402     return "ico";
00403 }
00404 
00405 bool ICOHandler::canRead(QIODevice *device)
00406 {
00407     if (!device) {
00408         qWarning("ICOHandler::canRead() called with no device");
00409         return false;
00410     }
00411 
00412     const qint64 oldPos = device->pos();
00413 
00414     char head[8];
00415     qint64 readBytes = device->read(head, sizeof(head));
00416     const bool readOk = readBytes == sizeof(head);
00417 
00418     if (device->isSequential()) {
00419         while (readBytes > 0)
00420             device->ungetChar(head[readBytes-- - 1]);
00421     } else {
00422         device->seek(oldPos);
00423     }
00424 
00425     if ( !readOk )
00426         return false;
00427 
00428     return head[2] == '\001' && head[3] == '\000' && // type should be 1
00429         ( head[6] == 16 || head[6] == 32 || head[6] == 64 ) && // width can only be one of those
00430         ( head[7] == 16 || head[7] == 32 || head[7] == 64 );   // same for height
00431 }
00432 
00433 class ICOPlugin : public QImageIOPlugin
00434 {
00435 public:
00436     QStringList keys() const;
00437     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00438     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00439 };
00440 
00441 QStringList ICOPlugin::keys() const
00442 {
00443     return QStringList() << "ico" << "ICO";
00444 }
00445 
00446 QImageIOPlugin::Capabilities ICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00447 {
00448     if (format == "ico" || format == "ICO")
00449         return Capabilities(CanRead);
00450     if (!format.isEmpty())
00451         return 0;
00452     if (!device->isOpen())
00453         return 0;
00454 
00455     Capabilities cap;
00456     if (device->isReadable() && ICOHandler::canRead(device))
00457         cap |= CanRead;
00458     return cap;
00459 }
00460 
00461 QImageIOHandler *ICOPlugin::create(QIODevice *device, const QByteArray &format) const
00462 {
00463     QImageIOHandler *handler = new ICOHandler;
00464     handler->setDevice(device);
00465     handler->setFormat(format);
00466     return handler;
00467 }
00468 
00469 Q_EXPORT_STATIC_PLUGIN(ICOPlugin)
00470 Q_EXPORT_PLUGIN2(ico, ICOPlugin)

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   WTF
  • KJSEmbed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  •   core
  • Phonon
  •   Backend
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal