diff options
Diffstat (limited to 'src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp')
-rw-r--r-- | src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp b/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp new file mode 100644 index 0000000..7fdf81f --- /dev/null +++ b/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qsvgiconengine.h" + +#ifndef QT_NO_SVGRENDERER + +#include "qpainter.h" +#include "qpixmap.h" +#include "qsvgrenderer.h" +#include "qpixmapcache.h" +#include "qstyle.h" +#include "qapplication.h" +#include "qstyleoption.h" +#include "qfileinfo.h" +#include <QAtomicInt> +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +class QSvgIconEnginePrivate : public QSharedData +{ +public: + QSvgIconEnginePrivate() + : svgBuffers(0), addedPixmaps(0) + { stepSerialNum(); } + + ~QSvgIconEnginePrivate() + { delete addedPixmaps; delete svgBuffers; } + + static int hashKey(QIcon::Mode mode, QIcon::State state) + { return (((mode)<<4)|state); } + + QString pmcKey(const QSize &size, QIcon::Mode mode, QIcon::State state) + { return QLatin1String("$qt_svgicon_") + + QString::number(serialNum, 16).append(QLatin1Char('_')) + + QString::number((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state, 16); } + + void stepSerialNum() + { serialNum = lastSerialNum.fetchAndAddRelaxed(1); }; + + void loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state); + + QHash<int, QString> svgFiles; + QHash<int, QByteArray> *svgBuffers; + QHash<int, QPixmap> *addedPixmaps; + int serialNum; + static QAtomicInt lastSerialNum; +}; + +QAtomicInt QSvgIconEnginePrivate::lastSerialNum; + +static inline int pmKey(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return ((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state); +} + +QSvgIconEngine::QSvgIconEngine() + : d(new QSvgIconEnginePrivate) +{ +} + +QSvgIconEngine::QSvgIconEngine(const QSvgIconEngine &other) + : QIconEngineV2(other), d(new QSvgIconEnginePrivate) +{ + d->svgFiles = other.d->svgFiles; + if (other.d->svgBuffers) + d->svgBuffers = new QHash<int, QByteArray>(*other.d->svgBuffers); + if (other.d->addedPixmaps) + d->addedPixmaps = new QHash<int, QPixmap>(*other.d->addedPixmaps); +} + + +QSvgIconEngine::~QSvgIconEngine() +{ +} + + +QSize QSvgIconEngine::actualSize(const QSize &size, QIcon::Mode mode, + QIcon::State state) +{ + if (d->addedPixmaps) { + QPixmap pm = d->addedPixmaps->value(d->hashKey(mode, state)); + if (!pm.isNull() && pm.size() == size) + return size; + } + + QSvgRenderer renderer; + d->loadDataForModeAndState(&renderer, mode, state); + if (renderer.isValid()) { + QSize defaultSize = renderer.defaultSize(); + if (!defaultSize.isNull()) + defaultSize.scale(size, Qt::KeepAspectRatio); + return defaultSize; + } else { + return QSize(); + } +} + +void QSvgIconEnginePrivate::loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state) +{ + QByteArray buf; + if (svgBuffers) { + buf = svgBuffers->value(hashKey(mode, state)); + if (buf.isEmpty()) + buf = svgBuffers->value(hashKey(QIcon::Normal, QIcon::Off)); + } + if (!buf.isEmpty()) { +#ifndef QT_NO_COMPRESS + buf = qUncompress(buf); +#endif + renderer->load(buf); + } else { + QString svgFile = svgFiles.value(hashKey(mode, state)); + if (svgFile.isEmpty()) + svgFile = svgFiles.value(hashKey(QIcon::Normal, QIcon::Off)); + if (!svgFile.isEmpty()) + renderer->load(svgFile); + } +} + +QPixmap QSvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode, + QIcon::State state) +{ + QPixmap pm; + + if (d->addedPixmaps) { + pm = d->addedPixmaps->value(d->hashKey(mode, state)); + if (!pm.isNull() && pm.size() == size) + return pm; + } + + QSvgRenderer renderer; + d->loadDataForModeAndState(&renderer, mode, state); + if (!renderer.isValid()) + return pm; + + QSize actualSize = renderer.defaultSize(); + if (!actualSize.isNull()) + actualSize.scale(size, Qt::KeepAspectRatio); + + QString pmckey(d->pmcKey(actualSize, mode, state)); + if (QPixmapCache::find(pmckey, pm)) + return pm; + + QImage img(actualSize, QImage::Format_ARGB32_Premultiplied); + img.fill(0x00000000); + QPainter p(&img); + renderer.render(&p); + p.end(); + pm = QPixmap::fromImage(img); + QStyleOption opt(0); + opt.palette = QApplication::palette(); + QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt); + if (!generated.isNull()) + pm = generated; + + if (!pm.isNull()) + QPixmapCache::insert(pmckey, pm); + + return pm; +} + + +void QSvgIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, + QIcon::State state) +{ + if (!d->addedPixmaps) + d->addedPixmaps = new QHash<int, QPixmap>; + d->stepSerialNum(); + d->addedPixmaps->insert(d->hashKey(mode, state), pixmap); +} + + +void QSvgIconEngine::addFile(const QString &fileName, const QSize &, + QIcon::Mode mode, QIcon::State state) +{ + if (!fileName.isEmpty()) { + QString abs = fileName; + if (fileName.at(0) != QLatin1Char(':')) + abs = QFileInfo(fileName).absoluteFilePath(); + if (abs.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive) +#ifndef QT_NO_COMPRESS + || abs.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive) + || abs.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) +#endif + { + QSvgRenderer renderer(abs); + if (renderer.isValid()) { + d->stepSerialNum(); + d->svgFiles.insert(d->hashKey(mode, state), abs); + } + } else { + QPixmap pm(abs); + if (!pm.isNull()) + addPixmap(pm, mode, state); + } + } +} + +void QSvgIconEngine::paint(QPainter *painter, const QRect &rect, + QIcon::Mode mode, QIcon::State state) +{ + painter->drawPixmap(rect, pixmap(rect.size(), mode, state)); +} + +QString QSvgIconEngine::key() const +{ + return QLatin1String("svg"); +} + +QIconEngineV2 *QSvgIconEngine::clone() const +{ + return new QSvgIconEngine(*this); +} + + +bool QSvgIconEngine::read(QDataStream &in) +{ + d = new QSvgIconEnginePrivate; + d->svgBuffers = new QHash<int, QByteArray>; + + if (in.version() >= QDataStream::Qt_4_4) { + int isCompressed; + QHash<int, QString> fileNames; // For memoryoptimization later + in >> fileNames >> isCompressed >> *d->svgBuffers; +#ifndef QT_NO_COMPRESS + if (!isCompressed) { + foreach(int key, d->svgBuffers->keys()) + d->svgBuffers->insert(key, qCompress(d->svgBuffers->value(key))); + } +#else + if (isCompressed) { + qWarning("QSvgIconEngine: Can not decompress SVG data"); + d->svgBuffers->clear(); + } +#endif + int hasAddedPixmaps; + in >> hasAddedPixmaps; + if (hasAddedPixmaps) { + d->addedPixmaps = new QHash<int, QPixmap>; + in >> *d->addedPixmaps; + } + } + else { + QPixmap pixmap; + QByteArray data; + uint mode; + uint state; + int num_entries; + + in >> data; + if (!data.isEmpty()) { +#ifndef QT_NO_COMPRESS + data = qUncompress(data); +#endif + if (!data.isEmpty()) + d->svgBuffers->insert(d->hashKey(QIcon::Normal, QIcon::Off), data); + } + in >> num_entries; + for (int i=0; i<num_entries; ++i) { + if (in.atEnd()) + return false; + in >> pixmap; + in >> mode; + in >> state; + // The pm list written by 4.3 is buggy and/or useless, so ignore. + //addPixmap(pixmap, QIcon::Mode(mode), QIcon::State(state)); + } + } + + return true; +} + + +bool QSvgIconEngine::write(QDataStream &out) const +{ + if (out.version() >= QDataStream::Qt_4_4) { + int isCompressed = 0; +#ifndef QT_NO_COMPRESS + isCompressed = 1; +#endif + QHash<int, QByteArray> svgBuffers; + if (d->svgBuffers) + svgBuffers = *d->svgBuffers; + foreach(int key, d->svgFiles.keys()) { + QByteArray buf; + QFile f(d->svgFiles.value(key)); + if (f.open(QIODevice::ReadOnly)) + buf = f.readAll(); +#ifndef QT_NO_COMPRESS + buf = qCompress(buf); +#endif + svgBuffers.insert(key, buf); + } + out << d->svgFiles << isCompressed << svgBuffers; + if (d->addedPixmaps) + out << (int)1 << *d->addedPixmaps; + else + out << (int)0; + } + else { + QByteArray buf; + if (d->svgBuffers) + buf = d->svgBuffers->value(d->hashKey(QIcon::Normal, QIcon::Off)); + if (buf.isEmpty()) { + QString svgFile = d->svgFiles.value(d->hashKey(QIcon::Normal, QIcon::Off)); + if (!svgFile.isEmpty()) { + QFile f(svgFile); + if (f.open(QIODevice::ReadOnly)) + buf = f.readAll(); + } + } +#ifndef QT_NO_COMPRESS + buf = qCompress(buf); +#endif + out << buf; + // 4.3 has buggy handling of added pixmaps, so don't write any + out << (int)0; + } + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SVGRENDERER |