00001
00002
00003
00004
00005
00006
00007
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
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
00043
00044 struct BMP_INFOHDR
00045 {
00046 static const Q_UINT32 Size = 40;
00047 Q_UINT32 biSize;
00048 Q_UINT32 biWidth;
00049 Q_UINT32 biHeight;
00050 Q_UINT16 biPlanes;
00051 Q_UINT16 biBitCount;
00052 enum Compression { RGB = 0 };
00053 Q_UINT32 biCompression;
00054 Q_UINT32 biSizeImage;
00055 Q_UINT32 biXPelsPerMeter;
00056 Q_UINT32 biYPelsPerMeter;
00057 Q_UINT32 biClrUsed;
00058 Q_UINT32 biClrImportant;
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
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
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
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
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
00167
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
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