/**************************************************************************** ** ** 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 #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 svgFiles; QHash *svgBuffers; QHash *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(*other.d->svgBuffers); if (other.d->addedPixmaps) d->addedPixmaps = new QHash(*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; 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; if (in.version() >= QDataStream::Qt_4_4) { int isCompressed; QHash 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; 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> 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 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