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

KImgIO

ico.cpp

Go to the documentation of this file.
00001 
00002 /*
00003  * $Id: ico.cpp 392281 2005-02-22 16:29:46Z orlovich $
00004  * kimgio import filter for MS Windows .ico files
00005  *
00006  * Distributed under the terms of the LGPL
00007  * Copyright (c) 2000 Malte Starostik <malte@kde.org>
00008  *
00009  */   
00010 
00011 #include <cstring>
00012 #include <cstdlib>
00013 #include <algorithm>
00014 #include <vector>
00015 
00016 #include <qimage.h>
00017 #include <qbitmap.h>
00018 #include <qapplication.h>
00019 #include <qmemarray.h>
00020 #include <qpaintdevicemetrics.h>
00021 
00022 #include <kdelibs_export.h>
00023 
00024 #include "ico.h"
00025 
00026 namespace
00027 {
00028     // Global header
00029     struct IcoHeader
00030     {
00031         enum Type { Icon = 1, Cursor };
00032         Q_UINT16 reserved;
00033         Q_UINT16 type;
00034         Q_UINT16 count;
00035     };
00036 
00037     inline QDataStream& operator >>( QDataStream& s, IcoHeader& h )
00038     {
00039         return s >> h.reserved >> h.type >> h.count;
00040     }
00041 
00042     // Based on qt_read_dib et al. from qimage.cpp
00043     // (c) 1992-2002 Trolltech AS.
00044     struct BMP_INFOHDR
00045     {
00046         static const Q_UINT32 Size = 40;
00047         Q_UINT32  biSize;                // size of this struct
00048         Q_UINT32  biWidth;               // pixmap width
00049         Q_UINT32  biHeight;              // pixmap height
00050         Q_UINT16  biPlanes;              // should be 1
00051         Q_UINT16  biBitCount;            // number of bits per pixel
00052         enum Compression { RGB = 0 };
00053         Q_UINT32  biCompression;         // compression method
00054         Q_UINT32  biSizeImage;           // size of image
00055         Q_UINT32  biXPelsPerMeter;       // horizontal resolution
00056         Q_UINT32  biYPelsPerMeter;       // vertical resolution
00057         Q_UINT32  biClrUsed;             // number of colors used
00058         Q_UINT32  biClrImportant;        // number of important colors
00059     };
00060     const Q_UINT32 BMP_INFOHDR::Size;
00061   
00062     QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi )
00063     {
00064         s >> bi.biSize;
00065         if ( bi.biSize == BMP_INFOHDR::Size )
00066         {
00067             s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
00068             s >> bi.biCompression >> bi.biSizeImage;
00069             s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
00070             s >> bi.biClrUsed >> bi.biClrImportant;
00071         }
00072         return s;
00073     }
00074 
00075 #if 0
00076     QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi )
00077     {
00078         s << bi.biSize;
00079         s << bi.biWidth << bi.biHeight;
00080         s << bi.biPlanes;
00081         s << bi.biBitCount;
00082         s << bi.biCompression;
00083         s << bi.biSizeImage;
00084         s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
00085         s << bi.biClrUsed << bi.biClrImportant;
00086         return s;
00087     }
00088 #endif
00089 
00090     // Header for every icon in the file
00091     struct IconRec
00092     {
00093         unsigned char width;
00094         unsigned char height;
00095         Q_UINT16 colors;
00096         Q_UINT16 hotspotX;
00097         Q_UINT16 hotspotY;
00098         Q_UINT32 size;
00099         Q_UINT32 offset;
00100     };
00101 
00102     inline QDataStream& operator >>( QDataStream& s, IconRec& r )
00103     {
00104         return s >> r.width >> r.height >> r.colors
00105                  >> r.hotspotX >> r.hotspotY >> r.size >> r.offset;
00106     }
00107 
00108     struct LessDifference
00109     {
00110         LessDifference( unsigned s, unsigned c )
00111             : size( s ), colors( c ) {}
00112 
00113         bool operator ()( const IconRec& lhs, const IconRec& rhs ) const
00114         {
00115             // closest size match precedes everything else
00116             if ( std::abs( int( lhs.width - size ) ) <
00117                  std::abs( int( rhs.width - size ) ) ) return true;
00118             else if ( std::abs( int( lhs.width - size ) ) >
00119                  std::abs( int( rhs.width - size ) ) ) return false;
00120             else if ( colors == 0 )
00121             {
00122                 // high/true color requested
00123                 if ( lhs.colors == 0 ) return true;
00124                 else if ( rhs.colors == 0 ) return false;
00125                 else return lhs.colors > rhs.colors;
00126             }
00127             else
00128             {
00129                 // indexed icon requested
00130                 if ( lhs.colors == 0 && rhs.colors == 0 ) return false;
00131                 else if ( lhs.colors == 0 ) return false;
00132                 else return std::abs( int( lhs.colors - colors ) ) <
00133                             std::abs( int( rhs.colors - colors ) );
00134             }
00135         }
00136         unsigned size;
00137         unsigned colors;
00138     };
00139 
00140     bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon )
00141     {
00142         BMP_INFOHDR header;
00143         stream >> header;
00144         if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size ||
00145              header.biSize > rec.size ||
00146              header.biCompression != BMP_INFOHDR::RGB ||
00147              ( header.biBitCount != 1 && header.biBitCount != 4 &&
00148                header.biBitCount != 8 && header.biBitCount != 24 &&
00149                header.biBitCount != 32 ) ) return false;
00150 
00151         unsigned paletteSize, paletteEntries;
00152 
00153         if (header.biBitCount > 8)
00154         {
00155             paletteEntries = 0;
00156             paletteSize    = 0;
00157         }
00158         else
00159         {
00160             paletteSize    = (1 << header.biBitCount);
00161             paletteEntries = paletteSize;
00162             if (header.biClrUsed && header.biClrUsed < paletteSize)
00163                 paletteEntries = header.biClrUsed;
00164         }
00165         
00166         // Always create a 32-bit image to get the mask right
00167         // Note: this is safe as rec.width, rec.height are bytes
00168         icon.create( rec.width, rec.height, 32 );
00169         if ( icon.isNull() ) return false;
00170         icon.setAlphaBuffer( true );
00171 
00172         QMemArray< QRgb > colorTable( paletteSize );
00173         
00174         colorTable.fill( QRgb( 0 ) );
00175         for ( unsigned i = 0; i < paletteEntries; ++i )
00176         {
00177             unsigned char rgb[ 4 ];
00178             stream.readRawBytes( reinterpret_cast< char* >( &rgb ),
00179                                  sizeof( rgb ) );
00180             colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] );
00181         }
00182 
00183         unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4;
00184         
00185         unsigned char* buf = new unsigned char[ bpl ];
00186         unsigned char** lines = icon.jumpTable();
00187         for ( unsigned y = rec.height; !stream.atEnd() && y--; )
00188         {
00189             stream.readRawBytes( reinterpret_cast< char* >( buf ), bpl );
00190             unsigned char* pixel = buf;
00191             QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
00192             switch ( header.biBitCount )
00193             {
00194                 case 1:
00195                     for ( unsigned x = 0; x < rec.width; ++x )
00196                         *p++ = colorTable[
00197                             ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ];
00198                     break;
00199                 case 4:
00200                     for ( unsigned x = 0; x < rec.width; ++x )
00201                         if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ];
00202                         else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ];
00203                     break;
00204                 case 8:
00205                     for ( unsigned x = 0; x < rec.width; ++x )
00206                         *p++ = colorTable[ pixel[ x ] ];
00207                     break;
00208                 case 24:
00209                     for ( unsigned x = 0; x < rec.width; ++x )
00210                         *p++ = qRgb( pixel[ 3 * x + 2 ],
00211                                      pixel[ 3 * x + 1 ],
00212                                      pixel[ 3 * x ] );
00213                     break;
00214                 case 32:
00215                     for ( unsigned x = 0; x < rec.width; ++x )
00216                         *p++ = qRgba( pixel[ 4 * x + 2 ],
00217                                       pixel[ 4 * x + 1 ],
00218                                       pixel[ 4 * x ],
00219                                       pixel[ 4 * x  + 3] );
00220                     break;
00221             }
00222         }
00223         delete[] buf;
00224 
00225         if ( header.biBitCount < 32 )
00226         {
00227             // Traditional 1-bit mask
00228             bpl = ( rec.width + 31 ) / 32 * 4;
00229             buf = new unsigned char[ bpl ];
00230             for ( unsigned y = rec.height; y--; )
00231             {
00232                 stream.readRawBytes( reinterpret_cast< char* >( buf ), bpl );
00233                 QRgb* p = reinterpret_cast< QRgb* >( lines[ y ] );
00234                 for ( unsigned x = 0; x < rec.width; ++x, ++p )
00235                     if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) )
00236                         *p &= RGB_MASK;
00237             }
00238             delete[] buf;
00239         }
00240         return true;
00241     }
00242 }
00243 
00244 extern "C" KDE_EXPORT void kimgio_ico_read( QImageIO* io )
00245 {
00246     QIODevice::Offset offset = io->ioDevice()->at();
00247 
00248     QDataStream stream( io->ioDevice() );
00249     stream.setByteOrder( QDataStream::LittleEndian );
00250     IcoHeader header;
00251     stream >> header;
00252     if ( stream.atEnd() || !header.count ||
00253          ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) )
00254         return;
00255 
00256     QPaintDeviceMetrics metrics( QApplication::desktop() );
00257     unsigned requestedSize = 32;
00258     unsigned requestedColors = metrics.depth() > 8 ? 0 : metrics.depth();
00259     int requestedIndex = -1;
00260     if ( io->parameters() )
00261     {
00262         QStringList params = QStringList::split( ';', io->parameters() );
00263         QMap< QString, QString > options;
00264         for ( QStringList::ConstIterator it = params.begin();
00265               it != params.end(); ++it )
00266         {
00267             QStringList tmp = QStringList::split( '=', *it );
00268             if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ];
00269         }
00270         if ( options[ "index" ].toUInt() )
00271             requestedIndex = options[ "index" ].toUInt();
00272         if ( options[ "size" ].toUInt() )
00273             requestedSize = options[ "size" ].toUInt();
00274         if ( options[ "colors" ].toUInt() )
00275             requestedColors = options[ "colors" ].toUInt();
00276     }
00277 
00278     typedef std::vector< IconRec > IconList;
00279     IconList icons;
00280     for ( unsigned i = 0; i < header.count; ++i )
00281     {
00282         if ( stream.atEnd() ) return;
00283         IconRec rec;
00284         stream >> rec;
00285         icons.push_back( rec );
00286     }
00287     IconList::const_iterator selected;
00288     if (requestedIndex >= 0) {
00289         selected = std::min( icons.begin() + requestedIndex, icons.end() );
00290     } else {
00291         selected = std::min_element( icons.begin(), icons.end(),
00292                           LessDifference( requestedSize, requestedColors ) );
00293     }
00294     if ( stream.atEnd() || selected == icons.end() ||
00295          offset + selected->offset > io->ioDevice()->size() )
00296         return;
00297 
00298     io->ioDevice()->at( offset + selected->offset );
00299     QImage icon;
00300     if ( loadFromDIB( stream, *selected, icon ) )
00301     {
00302         icon.setText( "X-Index", 0, QString::number( selected - icons.begin() ) );
00303         if ( header.type == IcoHeader::Cursor )
00304         {
00305             icon.setText( "X-HotspotX", 0, QString::number( selected->hotspotX ) );
00306             icon.setText( "X-HotspotY", 0, QString::number( selected->hotspotY ) );
00307         }
00308         io->setImage(icon);
00309         io->setStatus(0);
00310     }
00311 }
00312 
00313 #if 0
00314 void kimgio_ico_write(QImageIO *io)
00315 {
00316     if (io->image().isNull())
00317         return;
00318 
00319     QByteArray dibData;
00320     QDataStream dib(dibData, IO_ReadWrite);
00321     dib.setByteOrder(QDataStream::LittleEndian);
00322 
00323     QImage pixels = io->image();
00324     QImage mask;
00325     if (io->image().hasAlphaBuffer())
00326         mask = io->image().createAlphaMask();
00327     else
00328         mask = io->image().createHeuristicMask();
00329     mask.invertPixels();
00330     for ( int y = 0; y < pixels.height(); ++y )
00331         for ( int x = 0; x < pixels.width(); ++x )
00332             if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 );
00333 
00334     if (!qt_write_dib(dib, pixels))
00335         return;
00336 
00337    uint hdrPos = dib.device()->at();
00338     if (!qt_write_dib(dib, mask))
00339         return;
00340     memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8);
00341     dibData.resize(dibData.size() - BMP_WIN - 8);
00342         
00343     QDataStream ico(io->ioDevice());
00344     ico.setByteOrder(QDataStream::LittleEndian);
00345     IcoHeader hdr;
00346     hdr.reserved = 0;
00347     hdr.type = Icon;
00348     hdr.count = 1;
00349     ico << hdr.reserved << hdr.type << hdr.count;
00350     IconRec rec;
00351     rec.width = io->image().width();
00352     rec.height = io->image().height();
00353     if (io->image().numColors() <= 16)
00354         rec.colors = 16;
00355     else if (io->image().depth() <= 8)
00356         rec.colors = 256;
00357     else
00358         rec.colors = 0;
00359     rec.hotspotX = 0;
00360     rec.hotspotY = 0;
00361     rec.dibSize = dibData.size();
00362     ico << rec.width << rec.height << rec.colors
00363         << rec.hotspotX << rec.hotspotY << rec.dibSize;
00364     rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset);
00365     ico << rec.dibOffset;
00366 
00367     BMP_INFOHDR dibHeader;
00368     dib.device()->at(0);
00369     dib >> dibHeader;
00370     dibHeader.biHeight = io->image().height() << 1;
00371     dib.device()->at(0);
00372     dib << dibHeader;
00373 
00374     ico.writeRawBytes(dibData.data(), dibData.size());
00375     io->setStatus(0);
00376 }
00377 #endif

KImgIO

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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