Commit a933d565 authored by staikos's avatar staikos

Patch from Adam Treat to add ICO support to the Qt build.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@23691 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c1c68091
2007-06-15 Adam Treat <adam@staikos.net>
Reviewed by George Staikos.
Add ICO support to the Qt build.
* WebKit.pro:
2007-06-13 George Staikos <staikos@kde.org>
Reviewed by Lars.
......
2007-06-15 Adam Treat <adam@staikos.net>
Reviewed by George Staikos.
Add ICO support to the Qt build.
* WebCore.pro:
* platform/graphics/qt/ImageDecoderQt.cpp:
(Q_IMPORT_PLUGIN):
2007-06-15 George Staikos <staikos@kde.org>
Fixing Qt build.
......@@ -54,6 +54,9 @@ macx {
LIBS += -lxml2 -lxslt
}
QTPLUGIN += qtwebico
LIBS += -L$$OUTPUT_DIR/WebKitQt/Plugins
qt-port {
INCLUDEPATH += \
$$[QT_INSTALL_PREFIX]/src/3rdparty/sqlite/ \
......
......@@ -35,6 +35,8 @@
#include <QtGui/QImageReader>
#include <qdebug.h>
Q_IMPORT_PLUGIN(qtwebico) //For ico format...
namespace {
const QImage::Format DesiredFormat = QImage::Format_ARGB32;
const bool debugImageDecoderQt = false;
......
......@@ -2,6 +2,7 @@ TEMPLATE = subdirs
CONFIG += ordered
!gdk-port:CONFIG += qt-port
SUBDIRS = \
WebKitQt/Plugins \
WebCore \
JavaScriptCore/kjs/testkjs.pro
......
......@@ -42,6 +42,8 @@
#include "Page.h"
#include "FrameLoader.h"
#include "KURL.h"
#include "Image.h"
#include "IconDatabase.h"
#include <QDebug>
#include <QDragEnterEvent>
......@@ -343,6 +345,19 @@ QWebNetworkInterface *QWebPage::networkInterface() const
return QWebNetworkInterface::defaultInterface();
}
QPixmap QWebPage::icon() const
{
Image* image = iconDatabase()->iconForPageURL(url().toString(), IntSize(16, 16));
if (!image) {
return QPixmap();
}
QPixmap *icon = image->getPixmap();
Q_ASSERT(icon);
Q_ASSERT(!icon->isNull());
return *icon;
}
void QWebPage::setSettings(const QWebSettings &settings)
{
WebCore::Settings *wSettings = d->page->settings();
......
......@@ -83,6 +83,8 @@ public:
void setNetworkInterface(QWebNetworkInterface *interface);
QWebNetworkInterface *networkInterface() const;
QPixmap icon() const;
public slots:
/**
* Stops loading of the page, if loading.
......@@ -129,6 +131,10 @@ signals:
* Signal is emitted when the statusbar text is changed by the page.
*/
void statusBarTextChanged(const QString& text);
/**
* Signal is emitted when an icon ("favicon") is loaded from the site.
*/
void iconLoaded();
protected:
virtual QWebFrame *createFrame(QWebFrame *parentFrame, QWebFrameData *frameData);
......
......@@ -29,6 +29,7 @@
#include "Settings.h"
#include "KURL.h"
#include "PlatformString.h"
#include "IconDatabase.h"
#include <QHash>
#include <QSharedData>
......@@ -130,6 +131,25 @@ QString QWebSettings::userStyleSheetLocation() const
return d->userStyleSheetLocation;
}
void QWebSettings::setIconDatabaseEnabled(bool enabled, const QString &location)
{
WebCore::iconDatabase()->setEnabled(enabled);
if (enabled) {
if (!location.isEmpty()) {
WebCore::iconDatabase()->open(location);
} else {
WebCore::iconDatabase()->open(WebCore::iconDatabase()->defaultDatabaseFilename());
}
} else {
WebCore::iconDatabase()->close();
}
}
bool QWebSettings::iconDatabaseEnabled() const
{
return WebCore::iconDatabase()->enabled() && WebCore::iconDatabase()->isOpen();
}
QWebSettings::QWebSettings(const QWebSettings &other)
{
d = other.d;
......
......@@ -80,6 +80,9 @@ public:
void setUserStyleSheetLocation(const QString &location);
QString userStyleSheetLocation() const;
void setIconDatabaseEnabled(bool enabled, const QString &location = QString());
bool iconDatabaseEnabled() const;
private:
QSharedDataPointer<QWebSettingsPrivate> d;
};
......
2007-06-15 Adam Treat <adam@staikos.net>
Reviewed by George Staikos.
Add ICO support to the Qt build.
* Api/qwebpage.cpp:
(QWebPage::icon):
* Api/qwebpage.h:
* Api/qwebsettings.cpp:
(QWebSettings::setIconDatabaseEnabled):
(QWebSettings::iconDatabaseEnabled):
* Api/qwebsettings.h:
* Plugins: Added.
* Plugins/ICOHandler.cpp: Added.
(IcoHeader::operator >>):
(IcoHeader::BMP_INFOHDR::):
(IcoHeader::operator<<):
(IcoHeader::LessDifference::LessDifference):
(IcoHeader::LessDifference::operator ()):
(IcoHeader::loadFromDIB):
(ICOHandler::ICOHandler):
(ICOHandler::canRead):
(ICOHandler::read):
(ICOHandler::write):
(ICOHandler::name):
(ICOPlugin::keys):
(ICOPlugin::capabilities):
(ICOPlugin::create):
* Plugins/ICOHandler.h: Added.
* Plugins/Plugins.pro: Added.
* WebCoreSupport/FrameLoaderClientQt.cpp:
(WebCore::FrameLoaderClientQt::dispatchDidReceiveIcon):
2007-06-15 George Staikos <staikos@kde.org>
Fixing the Qt build.
......
/*
* kimgio import filter for MS Windows .ico files
*
* Distributed under the terms of the LGPL
* Copyright (c) 2000 Malte Starostik <malte@kde.org>
*
*/
// remove when QImage::jumpTable is ported
#define QT3_SUPPORT
#define QT3_SUPPORT_WARNINGS
#ifdef __GNUC__
#warning TODO: remove QT3_SUPPORT
#endif
#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 Trolltech AS.
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 ];
unsigned char** lines = icon.jumpTable();
for ( unsigned y = rec.height; !stream.atEnd() && y--; )
{
stream.readRawData( reinterpret_cast< char* >( buf ), bpl );
unsigned char* pixel = buf;
QRgb* p = reinterpret_cast< QRgb* >( lines[ 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* >( lines[ 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 ) )
{
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 ) );
}
*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;