2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
2019-06-03 14:21:30 +00:00
|
|
|
** Copyright (C) 2016-2019 Ivailo Monev
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
2019-06-03 13:38:02 +00:00
|
|
|
** This file is part of the plugins of the Katie Toolkit.
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** 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 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, 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.
|
|
|
|
**
|
|
|
|
** $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 <QtCore/qatomic.h>
|
|
|
|
#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;
|
|
|
|
|
|
|
|
QSvgIconEngine::QSvgIconEngine()
|
|
|
|
: d(new QSvgIconEnginePrivate)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QSvgIconEngine::QSvgIconEngine(const QSvgIconEngine &other)
|
2016-06-25 14:33:40 +00:00
|
|
|
: QIconEngine(other), d(new QSvgIconEnginePrivate)
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap pm = pixmap(size, mode, state);
|
|
|
|
if (pm.isNull())
|
|
|
|
return QSize();
|
|
|
|
return pm.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-06-28 20:40:35 +00:00
|
|
|
buf = qFastUncompress(buf);
|
2015-12-10 05:06:13 +02:00
|
|
|
#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;
|
|
|
|
|
|
|
|
QString pmckey(d->pmcKey(size, mode, state));
|
|
|
|
if (QPixmapCache::find(pmckey, pm))
|
|
|
|
return 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);
|
|
|
|
|
2016-01-13 16:40:44 +02:00
|
|
|
if (actualSize.isEmpty())
|
|
|
|
return QPixmap();
|
|
|
|
|
2015-12-10 05:06:13 +02:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-06-25 14:33:40 +00:00
|
|
|
QIconEngine *QSvgIconEngine::clone() const
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
return new QSvgIconEngine(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool QSvgIconEngine::read(QDataStream &in)
|
|
|
|
{
|
|
|
|
d = new QSvgIconEnginePrivate;
|
|
|
|
d->svgBuffers = new QHash<int, QByteArray>;
|
|
|
|
|
2017-08-10 04:39:48 +00:00
|
|
|
int isCompressed;
|
|
|
|
QHash<int, QString> fileNames; // For memoryoptimization later
|
|
|
|
in >> fileNames >> isCompressed >> *d->svgBuffers;
|
2015-12-10 05:06:13 +02:00
|
|
|
#ifndef QT_NO_COMPRESS
|
2017-08-10 04:39:48 +00:00
|
|
|
if (!isCompressed) {
|
|
|
|
foreach(int key, d->svgBuffers->keys())
|
2019-06-28 20:40:35 +00:00
|
|
|
d->svgBuffers->insert(key, qFastCompress(d->svgBuffers->value(key)));
|
2017-08-10 04:39:48 +00:00
|
|
|
}
|
2015-12-10 05:06:13 +02:00
|
|
|
#else
|
2017-08-10 04:39:48 +00:00
|
|
|
if (isCompressed) {
|
|
|
|
qWarning("QSvgIconEngine: Can not decompress SVG data");
|
|
|
|
d->svgBuffers->clear();
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
#endif
|
2017-08-10 04:39:48 +00:00
|
|
|
int hasAddedPixmaps;
|
|
|
|
in >> hasAddedPixmaps;
|
|
|
|
if (hasAddedPixmaps) {
|
|
|
|
d->addedPixmaps = new QHash<int, QPixmap>;
|
|
|
|
in >> *d->addedPixmaps;
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool QSvgIconEngine::write(QDataStream &out) const
|
|
|
|
{
|
2017-08-10 04:39:48 +00:00
|
|
|
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();
|
2016-01-13 16:50:53 +02:00
|
|
|
#ifndef QT_NO_COMPRESS
|
2019-06-28 20:40:35 +00:00
|
|
|
buf = qFastCompress(buf);
|
2016-01-13 16:50:53 +02:00
|
|
|
#endif
|
2017-08-10 04:39:48 +00:00
|
|
|
svgBuffers.insert(key, buf);
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
#ifndef QT_NO_COMPRESS
|
2017-08-10 04:39:48 +00:00
|
|
|
out << d->svgFiles << 1 << svgBuffers;
|
|
|
|
#else
|
|
|
|
out << d->svgFiles << 0 << svgBuffers;
|
2015-12-10 05:06:13 +02:00
|
|
|
#endif
|
2017-08-10 04:39:48 +00:00
|
|
|
if (d->addedPixmaps)
|
|
|
|
out << (int)1 << *d->addedPixmaps;
|
|
|
|
else
|
2015-12-10 05:06:13 +02:00
|
|
|
out << (int)0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
#endif // QT_NO_SVGRENDERER
|