diff options
Diffstat (limited to 'src/3rdparty/webkit/WebKit/qt/Plugins/ICOHandler.cpp')
-rw-r--r-- | src/3rdparty/webkit/WebKit/qt/Plugins/ICOHandler.cpp | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebKit/qt/Plugins/ICOHandler.cpp b/src/3rdparty/webkit/WebKit/qt/Plugins/ICOHandler.cpp new file mode 100644 index 0000000..44f3d44 --- /dev/null +++ b/src/3rdparty/webkit/WebKit/qt/Plugins/ICOHandler.cpp @@ -0,0 +1,459 @@ +/* + * kimgio import filter for MS Windows .ico files + * + * Distributed under the terms of the LGPL + * Copyright (c) 2000 Malte Starostik <malte@kde.org> + * + */ + +#include "ICOHandler.h" + +#include <cstring> +#include <cstdlib> +#include <algorithm> +#include <vector> + +#include <QtGui/QImage> +#include <QtGui/QBitmap> +#include <QtGui/QApplication> +#include <QtCore/QVector> +#include <QtGui/QDesktopWidget> + +namespace +{ + // Global header (see http://www.daubnet.com/formats/ICO.html) + struct IcoHeader + { + enum Type { Icon = 1, Cursor }; + quint16 reserved; + quint16 type; + quint16 count; + }; + + inline QDataStream& operator >>( QDataStream& s, IcoHeader& h ) + { + return s >> h.reserved >> h.type >> h.count; + } + + // Based on qt_read_dib et al. from qimage.cpp + // (c) 1992-2002 Nokia Corporation and/or its subsidiary(-ies). + struct BMP_INFOHDR + { + static const quint32 Size = 40; + quint32 biSize; // size of this struct + quint32 biWidth; // pixmap width + quint32 biHeight; // pixmap height + quint16 biPlanes; // should be 1 + quint16 biBitCount; // number of bits per pixel + enum Compression { RGB = 0 }; + quint32 biCompression; // compression method + quint32 biSizeImage; // size of image + quint32 biXPelsPerMeter; // horizontal resolution + quint32 biYPelsPerMeter; // vertical resolution + quint32 biClrUsed; // number of colors used + quint32 biClrImportant; // number of important colors + }; + const quint32 BMP_INFOHDR::Size; + + QDataStream& operator >>( QDataStream &s, BMP_INFOHDR &bi ) + { + s >> bi.biSize; + if ( bi.biSize == BMP_INFOHDR::Size ) + { + s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount; + s >> bi.biCompression >> bi.biSizeImage; + s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter; + s >> bi.biClrUsed >> bi.biClrImportant; + } + return s; + } + +#if 0 + QDataStream &operator<<( QDataStream &s, const BMP_INFOHDR &bi ) + { + s << bi.biSize; + s << bi.biWidth << bi.biHeight; + s << bi.biPlanes; + s << bi.biBitCount; + s << bi.biCompression; + s << bi.biSizeImage; + s << bi.biXPelsPerMeter << bi.biYPelsPerMeter; + s << bi.biClrUsed << bi.biClrImportant; + return s; + } +#endif + + // Header for every icon in the file + struct IconRec + { + unsigned char width; + unsigned char height; + quint16 colors; + quint16 hotspotX; + quint16 hotspotY; + quint32 size; + quint32 offset; + }; + + inline QDataStream& operator >>( QDataStream& s, IconRec& r ) + { + return s >> r.width >> r.height >> r.colors + >> r.hotspotX >> r.hotspotY >> r.size >> r.offset; + } + + struct LessDifference + { + LessDifference( unsigned s, unsigned c ) + : size( s ), colors( c ) {} + + bool operator ()( const IconRec& lhs, const IconRec& rhs ) const + { + // closest size match precedes everything else + if ( std::abs( int( lhs.width - size ) ) < + std::abs( int( rhs.width - size ) ) ) return true; + else if ( std::abs( int( lhs.width - size ) ) > + std::abs( int( rhs.width - size ) ) ) return false; + else if ( colors == 0 ) + { + // high/true color requested + if ( lhs.colors == 0 ) return true; + else if ( rhs.colors == 0 ) return false; + else return lhs.colors > rhs.colors; + } + else + { + // indexed icon requested + if ( lhs.colors == 0 && rhs.colors == 0 ) return false; + else if ( lhs.colors == 0 ) return false; + else return std::abs( int( lhs.colors - colors ) ) < + std::abs( int( rhs.colors - colors ) ); + } + } + unsigned size; + unsigned colors; + }; + + bool loadFromDIB( QDataStream& stream, const IconRec& rec, QImage& icon ) + { + BMP_INFOHDR header; + stream >> header; + if ( stream.atEnd() || header.biSize != BMP_INFOHDR::Size || + header.biSize > rec.size || + header.biCompression != BMP_INFOHDR::RGB || + ( header.biBitCount != 1 && header.biBitCount != 4 && + header.biBitCount != 8 && header.biBitCount != 24 && + header.biBitCount != 32 ) ) return false; + + unsigned paletteSize, paletteEntries; + + if (header.biBitCount > 8) + { + paletteEntries = 0; + paletteSize = 0; + } + else + { + paletteSize = (1 << header.biBitCount); + paletteEntries = paletteSize; + if (header.biClrUsed && header.biClrUsed < paletteSize) + paletteEntries = header.biClrUsed; + } + + // Always create a 32-bit image to get the mask right + // Note: this is safe as rec.width, rec.height are bytes + icon = QImage( rec.width, rec.height, QImage::Format_ARGB32 ); + if ( icon.isNull() ) return false; + + QVector< QRgb > colorTable( paletteSize ); + + colorTable.fill( QRgb( 0 ) ); + for ( unsigned i = 0; i < paletteEntries; ++i ) + { + unsigned char rgb[ 4 ]; + stream.readRawData( reinterpret_cast< char* >( &rgb ), + sizeof( rgb ) ); + colorTable[ i ] = qRgb( rgb[ 2 ], rgb[ 1 ], rgb[ 0 ] ); + } + + unsigned bpl = ( rec.width * header.biBitCount + 31 ) / 32 * 4; + + unsigned char* buf = new unsigned char[ bpl ]; + for ( unsigned y = rec.height; !stream.atEnd() && y--; ) + { + stream.readRawData( reinterpret_cast< char* >( buf ), bpl ); + unsigned char* pixel = buf; + QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine( y ) ); + switch ( header.biBitCount ) + { + case 1: + for ( unsigned x = 0; x < rec.width; ++x ) + *p++ = colorTable[ + ( pixel[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ]; + break; + case 4: + for ( unsigned x = 0; x < rec.width; ++x ) + if ( x & 1 ) *p++ = colorTable[ pixel[ x / 2 ] & 0x0f ]; + else *p++ = colorTable[ pixel[ x / 2 ] >> 4 ]; + break; + case 8: + for ( unsigned x = 0; x < rec.width; ++x ) + *p++ = colorTable[ pixel[ x ] ]; + break; + case 24: + for ( unsigned x = 0; x < rec.width; ++x ) + *p++ = qRgb( pixel[ 3 * x + 2 ], + pixel[ 3 * x + 1 ], + pixel[ 3 * x ] ); + break; + case 32: + for ( unsigned x = 0; x < rec.width; ++x ) + *p++ = qRgba( pixel[ 4 * x + 2 ], + pixel[ 4 * x + 1 ], + pixel[ 4 * x ], + pixel[ 4 * x + 3] ); + break; + } + } + delete[] buf; + + if ( header.biBitCount < 32 ) + { + // Traditional 1-bit mask + bpl = ( rec.width + 31 ) / 32 * 4; + buf = new unsigned char[ bpl ]; + for ( unsigned y = rec.height; y--; ) + { + stream.readRawData( reinterpret_cast< char* >( buf ), bpl ); + QRgb* p = reinterpret_cast< QRgb* >( icon.scanLine( y ) ); + for ( unsigned x = 0; x < rec.width; ++x, ++p ) + if ( ( ( buf[ x / 8 ] >> ( 7 - ( x & 0x07 ) ) ) & 1 ) ) + *p &= RGB_MASK; + } + delete[] buf; + } + return true; + } +} + +ICOHandler::ICOHandler() +{ +} + +bool ICOHandler::canRead() const +{ + return canRead(device()); +} + +bool ICOHandler::read(QImage *outImage) +{ + + qint64 offset = device()->pos(); + + QDataStream stream( device() ); + stream.setByteOrder( QDataStream::LittleEndian ); + IcoHeader header; + stream >> header; + if ( stream.atEnd() || !header.count || + ( header.type != IcoHeader::Icon && header.type != IcoHeader::Cursor) ) + return false; + + unsigned requestedSize = 32; + unsigned requestedColors = QApplication::desktop()->depth() > 8 ? 0 : QApplication::desktop()->depth(); + int requestedIndex = -1; +#if 0 + if ( io->parameters() ) + { + QStringList params = QString(io->parameters()).split( ';', QString::SkipEmptyParts ); + QMap< QString, QString > options; + for ( QStringList::ConstIterator it = params.begin(); + it != params.end(); ++it ) + { + QStringList tmp = (*it).split( '=', QString::SkipEmptyParts ); + if ( tmp.count() == 2 ) options[ tmp[ 0 ] ] = tmp[ 1 ]; + } + if ( options[ "index" ].toUInt() ) + requestedIndex = options[ "index" ].toUInt(); + if ( options[ "size" ].toUInt() ) + requestedSize = options[ "size" ].toUInt(); + if ( options[ "colors" ].toUInt() ) + requestedColors = options[ "colors" ].toUInt(); + } +#endif + + typedef std::vector< IconRec > IconList; + IconList icons; + for ( unsigned i = 0; i < header.count; ++i ) + { + if ( stream.atEnd() ) + return false; + IconRec rec; + stream >> rec; + icons.push_back( rec ); + } + IconList::const_iterator selected; + if (requestedIndex >= 0) { + selected = std::min( icons.begin() + requestedIndex, icons.end() ); + } else { + selected = std::min_element( icons.begin(), icons.end(), + LessDifference( requestedSize, requestedColors ) ); + } + if ( stream.atEnd() || selected == icons.end() || + offset + selected->offset > device()->size() ) + return false; + + device()->seek( offset + selected->offset ); + QImage icon; + if ( loadFromDIB( stream, *selected, icon ) ) + { +#ifndef QT_NO_IMAGE_TEXT + icon.setText( "X-Index", 0, QString::number( selected - icons.begin() ) ); + if ( header.type == IcoHeader::Cursor ) + { + icon.setText( "X-HotspotX", 0, QString::number( selected->hotspotX ) ); + icon.setText( "X-HotspotY", 0, QString::number( selected->hotspotY ) ); + } +#endif + *outImage = icon; + return true; + } + return false; +} + +bool ICOHandler::write(const QImage &/*image*/) +{ +#if 0 + if (image.isNull()) + return; + + QByteArray dibData; + QDataStream dib(dibData, QIODevice::ReadWrite); + dib.setByteOrder(QDataStream::LittleEndian); + + QImage pixels = image; + QImage mask; + if (io->image().hasAlphaBuffer()) + mask = image.createAlphaMask(); + else + mask = image.createHeuristicMask(); + mask.invertPixels(); + for ( int y = 0; y < pixels.height(); ++y ) + for ( int x = 0; x < pixels.width(); ++x ) + if ( mask.pixel( x, y ) == 0 ) pixels.setPixel( x, y, 0 ); + + if (!qt_write_dib(dib, pixels)) + return; + + uint hdrPos = dib.device()->at(); + if (!qt_write_dib(dib, mask)) + return; + memmove(dibData.data() + hdrPos, dibData.data() + hdrPos + BMP_WIN + 8, dibData.size() - hdrPos - BMP_WIN - 8); + dibData.resize(dibData.size() - BMP_WIN - 8); + + QDataStream ico(device()); + ico.setByteOrder(QDataStream::LittleEndian); + IcoHeader hdr; + hdr.reserved = 0; + hdr.type = Icon; + hdr.count = 1; + ico << hdr.reserved << hdr.type << hdr.count; + IconRec rec; + rec.width = image.width(); + rec.height = image.height(); + if (image.numColors() <= 16) + rec.colors = 16; + else if (image.depth() <= 8) + rec.colors = 256; + else + rec.colors = 0; + rec.hotspotX = 0; + rec.hotspotY = 0; + rec.dibSize = dibData.size(); + ico << rec.width << rec.height << rec.colors + << rec.hotspotX << rec.hotspotY << rec.dibSize; + rec.dibOffset = ico.device()->at() + sizeof(rec.dibOffset); + ico << rec.dibOffset; + + BMP_INFOHDR dibHeader; + dib.device()->at(0); + dib >> dibHeader; + dibHeader.biHeight = image.height() << 1; + dib.device()->at(0); + dib << dibHeader; + + ico.writeRawBytes(dibData.data(), dibData.size()); + return true; +#endif + return false; +} + +QByteArray ICOHandler::name() const +{ + return "ico"; +} + +bool ICOHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("ICOHandler::canRead() called with no device"); + return false; + } + + const qint64 oldPos = device->pos(); + + char head[8]; + qint64 readBytes = device->read(head, sizeof(head)); + const bool readOk = readBytes == sizeof(head); + + if (device->isSequential()) { + while (readBytes > 0) + device->ungetChar(head[readBytes-- - 1]); + } else { + device->seek(oldPos); + } + + if ( !readOk ) + return false; + + return head[2] == '\001' && head[3] == '\000' && // type should be 1 + ( head[6] == 16 || head[6] == 32 || head[6] == 64 ) && // width can only be one of those + ( head[7] == 16 || head[7] == 32 || head[7] == 64 ); // same for height +} + +class ICOPlugin : public QImageIOPlugin +{ +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList ICOPlugin::keys() const +{ + return QStringList() << "ico" << "ICO"; +} + +QImageIOPlugin::Capabilities ICOPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "ico" || format == "ICO") + return Capabilities(CanRead); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && ICOHandler::canRead(device)) + cap |= CanRead; + return cap; +} + +QImageIOHandler *ICOPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new ICOHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +Q_EXPORT_STATIC_PLUGIN(ICOPlugin) +Q_EXPORT_PLUGIN2(qtwebico, ICOPlugin) |