Draft: Replace QString cache IDs with a struct-based version

As it turns out, QString::number() is quite expensive, especially when
using it in a code path that is called a lot of times. So instead, use a
struct with a custom hash method as cache ID. This is significantly
faster since we do not need to do memory allocations or string
conversions.
This commit is contained in:
Marco Martin 2020-12-17 11:31:27 +00:00
parent b1b91fb3eb
commit f09b46bec6
11 changed files with 403 additions and 256 deletions

View File

@ -33,6 +33,7 @@ target_link_libraries(corebindingsplugin
KF5::Declarative KF5::Declarative
KF5::IconThemes KF5::IconThemes
KF5::I18n KF5::I18n
Qt5::Svg
KF5::Service #for kplugininfo.h KF5::Service #for kplugininfo.h
KF5::WindowSystem KF5::WindowSystem
KF5::Plasma KF5::Plasma

View File

@ -83,7 +83,7 @@ ecm_generate_export_header(KF5Plasma
GROUP_BASE_NAME KF GROUP_BASE_NAME KF
VERSION ${KF5_VERSION} VERSION ${KF5_VERSION}
DEPRECATED_BASE_VERSION 0 DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 5.6 5.19 5.28 5.30 5.36 5.46 5.67 5.77 DEPRECATION_VERSIONS 5.6 5.19 5.28 5.30 5.36 5.46 5.67 5.77 5.78
) )
# TODO: add support for EXCLUDE_DEPRECATED_BEFORE_AND_AT to all Plasma libs # TODO: add support for EXCLUDE_DEPRECATED_BEFORE_AND_AT to all Plasma libs
# needs fixing of undeprecated API being still implemented using own deprecated API # needs fixing of undeprecated API being still implemented using own deprecated API

View File

@ -27,7 +27,7 @@
namespace Plasma namespace Plasma
{ {
QHash<ThemePrivate *, QHash<QString, QWeakPointer<FrameData>> > FrameSvgPrivate::s_sharedFrames; QHash<ThemePrivate *, QHash<uint, QWeakPointer<FrameData>> > FrameSvgPrivate::s_sharedFrames;
// Any attempt to generate a frame whose width or height is larger than this // Any attempt to generate a frame whose width or height is larger than this
// will be rejected // will be rejected
@ -332,7 +332,7 @@ QRegion FrameSvg::mask() const
return result; return result;
} }
QString id = d->cacheId(d->frame.data(), QString()); uint id = qHash(d->cacheId(d->frame.data(), QString()), SvgRectsCache::s_seed);
QRegion* obj = d->frame->cachedMasks.object(id); QRegion* obj = d->frame->cachedMasks.object(id);
@ -459,7 +459,7 @@ QPixmap FrameSvgPrivate::alphaMask()
QSharedPointer<FrameData> FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer<FrameData> &frame, const QString &maskPrefix, const QString &maskRequestedPrefix) QSharedPointer<FrameData> FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer<FrameData> &frame, const QString &maskPrefix, const QString &maskRequestedPrefix)
{ {
const QString key = cacheId(frame.data(), maskPrefix); const uint key = qHash(cacheId(frame.data(), maskPrefix));
QSharedPointer<FrameData> mask = s_sharedFrames[q->theme()->d].value(key); QSharedPointer<FrameData> mask = s_sharedFrames[q->theme()->d].value(key);
// See if we can find a suitable candidate in the shared frames. // See if we can find a suitable candidate in the shared frames.
@ -488,17 +488,19 @@ void FrameSvgPrivate::generateBackground(const QSharedPointer<FrameData> &frame)
return; return;
} }
const QString id = cacheId(frame.data(), frame->prefix); const uint id = qHash(cacheId(frame.data(), frame->prefix));
bool frameCached = !frame->cachedBackground.isNull(); bool frameCached = !frame->cachedBackground.isNull();
bool overlayCached = false; bool overlayCached = false;
//TODO KF6: Kill Overlays
const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay")); const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay"));
QPixmap overlay; QPixmap overlay;
if (q->isUsingRenderingCache()) { if (q->isUsingRenderingCache()) {
frameCached = q->theme()->findInCache(id, frame->cachedBackground, frame->lastModified) && !frame->cachedBackground.isNull(); frameCached = q->theme()->findInCache(QString::number(id), frame->cachedBackground, frame->lastModified) && !frame->cachedBackground.isNull();
if (overlayAvailable) { if (overlayAvailable) {
overlayCached = q->theme()->findInCache(QLatin1String("overlay_") % id, overlay, frame->lastModified) && !overlay.isNull(); const uint overlayId = qHash(cacheId(frame.data(), frame->prefix % QLatin1String("overlay")));
overlayCached = q->theme()->findInCache(QString::number(overlayId), overlay, frame->lastModified) && !overlay.isNull();
} }
} }
@ -622,10 +624,10 @@ QRect FrameSvgPrivate::contentGeometry(const QSharedPointer<FrameData> &frame, c
void FrameSvgPrivate::updateFrameData(uint lastModified, UpdateType updateType) void FrameSvgPrivate::updateFrameData(uint lastModified, UpdateType updateType)
{ {
auto fd = frame; auto fd = frame;
QString newKey; uint newKey = 0;
if (fd) { if (fd) {
const QString oldKey = fd->cacheId; const uint oldKey = fd->cacheId;
const QString oldPath = fd->imagePath; const QString oldPath = fd->imagePath;
const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders; const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders;
@ -635,7 +637,7 @@ void FrameSvgPrivate::updateFrameData(uint lastModified, UpdateType updateType)
fd->frameSize = pendingFrameSize; fd->frameSize = pendingFrameSize;
fd->imagePath = q->imagePath(); fd->imagePath = q->imagePath();
newKey = cacheId(fd.data(), prefix); newKey = qHash(cacheId(fd.data(), prefix));
//reset frame to old values //reset frame to old values
fd->enabledBorders = oldBorders; fd->enabledBorders = oldBorders;
@ -671,8 +673,8 @@ void FrameSvgPrivate::updateFrameData(uint lastModified, UpdateType updateType)
fd->imagePath = q->imagePath(); fd->imagePath = q->imagePath();
fd->lastModified = lastModified; fd->lastModified = lastModified;
//was fd just created empty now? //was fd just created empty now?
if (newKey.isEmpty()) { if (newKey == 0) {
newKey = cacheId(fd.data(), prefix); newKey = qHash(cacheId(fd.data(), prefix));
} }
// we know it isn't in s_sharedFrames due to the check above, so insert it now // we know it isn't in s_sharedFrames due to the check above, so insert it now
@ -752,11 +754,10 @@ void FrameSvgPrivate::paintCorner(QPainter& p, const QSharedPointer<FrameData> &
} }
} }
QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const SvgPrivate::CacheId FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
{ {
const QSize size = frameSize(frame).toSize(); const QSize size = frameSize(frame).toSize();
const QLatin1Char s('_'); return SvgPrivate::CacheId{double(size.width()), double(size.height()), frame->imagePath, prefixToSave, q->status(), q->scaleFactor(), q->devicePixelRatio(), q->colorGroup(), frame->enabledBorders, q->Svg::d->lastModified};
return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % QString::number(q->scaleFactor()) % s % QString::number(q->devicePixelRatio()) % s % prefixToSave % s % frame->imagePath;
} }
void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay)
@ -770,15 +771,16 @@ void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &bac
return; return;
} }
const QString id = cacheId(frame.data(), prefixToSave); const uint id = qHash(cacheId(frame.data(), prefixToSave));
//qCDebug(LOG_PLASMA)<<"Saving to cache frame"<<id; //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<<id;
q->theme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); q->theme()->insertIntoCache(QString::number(id), background, QString::number((qint64)q, 16) % prefixToSave);
if (!overlay.isNull()) { if (!overlay.isNull()) {
//insert overlay //insert overlay
q->theme()->insertIntoCache(QLatin1String("overlay_") % id, overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay")); const uint overlayId = qHash(cacheId(frame.data(), frame->prefix % QLatin1String("overlay")));
q->theme()->insertIntoCache(QString::number(overlayId), overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay"));
} }
} }

View File

@ -16,6 +16,8 @@
#include <Plasma/Theme> #include <Plasma/Theme>
#include "svg_p.h"
namespace Plasma namespace Plasma
{ {
@ -73,12 +75,12 @@ public:
QString requestedPrefix; QString requestedPrefix;
FrameSvg::EnabledBorders enabledBorders; FrameSvg::EnabledBorders enabledBorders;
QPixmap cachedBackground; QPixmap cachedBackground;
QCache<QString, QRegion> cachedMasks; QCache<uint, QRegion> cachedMasks;
static const int MAX_CACHED_MASKS = 10; static const int MAX_CACHED_MASKS = 10;
uint lastModified = 0; uint lastModified = 0;
QSize frameSize; QSize frameSize;
QString cacheId; uint cacheId;
//measures //measures
int topHeight; int topHeight;
@ -145,7 +147,7 @@ public:
void generateBackground(const QSharedPointer<FrameData> &frame); void generateBackground(const QSharedPointer<FrameData> &frame);
void generateFrameBackground(const QSharedPointer<FrameData> &); void generateFrameBackground(const QSharedPointer<FrameData> &);
QString cacheId(FrameData *frame, const QString &prefixToUse) const; SvgPrivate::CacheId cacheId(FrameData *frame, const QString &prefixToUse) const;
void cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay); void cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay);
void updateSizes(FrameData* frame) const; void updateSizes(FrameData* frame) const;
void updateSizes(const QSharedPointer<FrameData> &frame) const { return updateSizes(frame.data()); } void updateSizes(const QSharedPointer<FrameData> &frame) const { return updateSizes(frame.data()); }
@ -178,7 +180,7 @@ public:
//this can differ from frame->frameSize if we are in a transition //this can differ from frame->frameSize if we are in a transition
QSize pendingFrameSize; QSize pendingFrameSize;
static QHash<ThemePrivate *, QHash<QString, QWeakPointer<FrameData>> > s_sharedFrames; static QHash<ThemePrivate *, QHash<uint, QWeakPointer<FrameData>> > s_sharedFrames;
bool cacheAll : 1; bool cacheAll : 1;
bool repaintBlocked : 1; bool repaintBlocked : 1;

View File

@ -48,11 +48,24 @@ private:
class SvgPrivate class SvgPrivate
{ {
public: public:
struct CacheId {
double width;
double height;
QString filePath;
QString elementName;
int status;
double devicePixelRatio;
double scaleFactor;
int colorGroup;
uint extraFlags; //Not used here, used for enabledborders in FrameSvg
uint lastModified;
};
SvgPrivate(Svg *svg); SvgPrivate(Svg *svg);
~SvgPrivate(); ~SvgPrivate();
//This function is meant for the rects cache //This function is meant for the rects cache
QString cacheId(const QString &elementId) const; CacheId cacheId(const QString &elementId) const;
//This function is meant for the pixmap cache //This function is meant for the pixmap cache
QString cachePath(const QString &path, const QSize &size) const; QString cachePath(const QString &path, const QSize &size) const;
@ -68,7 +81,7 @@ public:
void eraseRenderer(); void eraseRenderer();
QRectF elementRect(const QString &elementId); QRectF elementRect(const QString &elementId);
QRectF findAndCacheElementRect(const QString &elementId, const QString &cacheId); QRectF findAndCacheElementRect(const QString &elementId);
void checkColorHints(); void checkColorHints();
@ -88,8 +101,6 @@ public:
Svg *q; Svg *q;
QPointer<Theme> theme; QPointer<Theme> theme;
QHash<QString, QRectF> localRectCache;
QMultiHash<QString, QSize> elementsWithSizeHints;
SharedSvgRenderer::Ptr renderer; SharedSvgRenderer::Ptr renderer;
QString themePath; QString themePath;
QString path; QString path;
@ -111,7 +122,56 @@ public:
bool themeFailed : 1; bool themeFailed : 1;
}; };
class SvgRectsCache : public QObject {
Q_OBJECT
public:
SvgRectsCache(QObject *parent = nullptr);
static SvgRectsCache *instance();
void insert(SvgPrivate::CacheId cacheId, const QRectF &rect, unsigned int lastModified);
void insert(uint id, const QString &filePath, const QRectF &rect, unsigned int lastModified);
// Those 2 methods are the same, the second uses the integer id produced by hashed CacheId
bool findElementRect(SvgPrivate::CacheId cacheId, QRectF &rect);
bool findElementRect(uint id, const QString &filePath, QRectF &rect);
void loadImageFromCache(const QString &path, uint lastModified);
void dropImageFromCache(const QString &path);
void expireCache(const QString &path);
void setNaturalSize(const QString &path, qreal scaleFactor, const QSizeF &size);
QSizeF naturalSize(const QString &path, qreal scaleFactor);
QList<QSize> sizeHintsForId(const QString &path, const QString &id);
void insertSizeHintForId(const QString &path, const QString &id, const QSize &size);
QString iconThemePath();
void setIconThemePath(const QString &path);
QStringList cachedKeysForPath(const QString &path) const;
void updateLastModified(const QString &filePath, unsigned int lastModified);
static const uint s_seed;
private:
QTimer *m_configSyncTimer = nullptr;
QString m_iconThemePath;
KSharedConfigPtr m_svgElementsCache;
/*
* We are indexing in the hash cache ids by their "digested" uint out of qHash(CacheId)
* because we need to serialize it and unserialize it to a config file,
* which is more efficient to do that with the uint directly rather than a CacheId struct serialization
*/
QHash<uint, QRectF> m_localRectCache;
QHash<QString, QSet<unsigned int>> m_invalidElements;
QHash<QString, QList<QSize>> m_sizeHintsForId;
};
} }
uint qHash(const Plasma::SvgPrivate::CacheId &id, uint seed = 0);
#endif #endif

View File

@ -8,6 +8,7 @@
#include "theme_p.h" #include "theme_p.h"
#include "framesvg.h" #include "framesvg.h"
#include "framesvg_p.h" #include "framesvg_p.h"
#include "svg_p.h"
#include "debug_p.h" #include "debug_p.h"
#include <QGuiApplication> #include <QGuiApplication>
@ -75,12 +76,6 @@ ThemePrivate::ThemePrivate(QObject *parent)
pixmapSaveTimer->setInterval(600); pixmapSaveTimer->setInterval(600);
QObject::connect(pixmapSaveTimer, &QTimer::timeout, this, &ThemePrivate::scheduledCacheUpdate); QObject::connect(pixmapSaveTimer, &QTimer::timeout, this, &ThemePrivate::scheduledCacheUpdate);
rectSaveTimer = new QTimer(this);
rectSaveTimer->setSingleShot(true);
//2 minutes
rectSaveTimer->setInterval(2 * 60 * 1000);
QObject::connect(rectSaveTimer, &QTimer::timeout, this, &ThemePrivate::saveSvgElementsCache);
updateNotificationTimer = new QTimer(this); updateNotificationTimer = new QTimer(this);
updateNotificationTimer->setSingleShot(true); updateNotificationTimer->setSingleShot(true);
updateNotificationTimer->setInterval(100); updateNotificationTimer->setInterval(100);
@ -121,7 +116,6 @@ ThemePrivate::ThemePrivate(QObject *parent)
ThemePrivate::~ThemePrivate() ThemePrivate::~ThemePrivate()
{ {
saveSvgElementsCache();
FrameSvgPrivate::s_sharedFrames.remove(this); FrameSvgPrivate::s_sharedFrames.remove(this);
delete pixmapCache; delete pixmapCache;
} }
@ -239,43 +233,24 @@ bool ThemePrivate::useCache()
ThemeConfig config; ThemeConfig config;
pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024); pixmapCache = new KImageCache(cacheFile, config.themeCacheKb() * 1024);
pixmapCache->setEvictionPolicy(KSharedDataCache::EvictLeastRecentlyUsed);
if (cachesTooOld) { if (cachesTooOld) {
discardCache(PixmapCache | SvgElementsCache); discardCache(PixmapCache | SvgElementsCache);
} }
} }
if (cacheTheme && !svgElementsCache) { if (cacheTheme) {
const QString svgElementsFileNameBase = QLatin1String("plasma-svgelements-") + themeName;
QString svgElementsFileName = svgElementsFileNameBase;
if (!themeVersion.isEmpty()) {
svgElementsFileName += QLatin1String("_v") + themeVersion;
}
// now we check for (and remove) old caches
QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation));
cacheDir.setNameFilters(QStringList({svgElementsFileNameBase + QLatin1Char('*')}));
const auto files = cacheDir.entryInfoList();
for (const QFileInfo &file : files) {
if (!file.absoluteFilePath().endsWith(svgElementsFileName)) {
QFile::remove(file.absoluteFilePath());
}
}
const QString svgElementsFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + svgElementsFileName;
svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig);
QString currentIconThemePath; QString currentIconThemePath;
const auto *iconTheme = KIconLoader::global()->theme(); const auto *iconTheme = KIconLoader::global()->theme();
if (iconTheme) { if (iconTheme) {
currentIconThemePath = iconTheme->dir(); currentIconThemePath = iconTheme->dir();
} }
KConfigGroup globalGroup(svgElementsCache, QLatin1String("Global"));
const QString oldIconThemePath = globalGroup.readEntry("currentIconThemePath", QString()); const QString oldIconThemePath = SvgRectsCache::instance()->iconThemePath();
if (oldIconThemePath != currentIconThemePath) { if (oldIconThemePath != currentIconThemePath) {
discardCache(PixmapCache | SvgElementsCache); discardCache(PixmapCache | SvgElementsCache);
globalGroup.writeEntry("currentIconThemePath", currentIconThemePath); SvgRectsCache::instance()->setIconThemePath(currentIconThemePath);
svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig);
} }
} }
@ -357,9 +332,6 @@ void ThemePrivate::discardCache(CacheTypes caches)
if (caches & SvgElementsCache) { if (caches & SvgElementsCache) {
discoveries.clear(); discoveries.clear();
invalidElements.clear();
svgElementsCache = nullptr;
} }
} }
@ -657,21 +629,6 @@ void ThemePrivate::settingsChanged(bool emitChanges)
setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false, emitChanges); setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false, emitChanges);
} }
void ThemePrivate::saveSvgElementsCache()
{
if (svgElementsCache) {
QHashIterator<QString, QSet<QString> > it(invalidElements);
while (it.hasNext()) {
it.next();
KConfigGroup imageGroup(svgElementsCache, it.key());
imageGroup.writeEntry("invalidElements", it.value().values()); //FIXME: add QSet support to KConfig
}
//Pretty drastic, but this is executed only very rarely
svgElementsCache->sync();
}
}
QColor ThemePrivate::color(Theme::ColorRole role, Theme::ColorGroup group) const QColor ThemePrivate::color(Theme::ColorRole role, Theme::ColorGroup group) const
{ {
const KColorScheme *scheme = nullptr; const KColorScheme *scheme = nullptr;

View File

@ -78,7 +78,6 @@ public Q_SLOTS:
void onAppExitCleanup(); void onAppExitCleanup();
void notifyOfChanged(); void notifyOfChanged();
void settingsChanged(bool emitChanges); void settingsChanged(bool emitChanges);
void saveSvgElementsCache();
Q_SIGNALS: Q_SIGNALS:
void themeChanged(); void themeChanged();
@ -116,9 +115,7 @@ public:
int defaultWallpaperWidth; int defaultWallpaperWidth;
int defaultWallpaperHeight; int defaultWallpaperHeight;
KImageCache *pixmapCache; KImageCache *pixmapCache;
KSharedConfigPtr svgElementsCache;
QString cachedDefaultStyleSheet; QString cachedDefaultStyleSheet;
QHash<QString, QSet<QString> > invalidElements;
QHash<QString, QPixmap> pixmapsToCache; QHash<QString, QPixmap> pixmapsToCache;
QHash<QString, QString> keysToCache; QHash<QString, QString> keysToCache;
QHash<QString, QString> idsToCache; QHash<QString, QString> idsToCache;
@ -126,7 +123,6 @@ public:
QHash<Theme::ColorGroup, QString> cachedSelectedSvgStyleSheets; QHash<Theme::ColorGroup, QString> cachedSelectedSvgStyleSheets;
QHash<QString, QString> discoveries; QHash<QString, QString> discoveries;
QTimer *pixmapSaveTimer; QTimer *pixmapSaveTimer;
QTimer *rectSaveTimer;
QTimer *updateNotificationTimer; QTimer *updateNotificationTimer;
unsigned cacheSize; unsigned cacheSize;
CacheTypes cachesToDiscard; CacheTypes cachesToDiscard;

View File

@ -34,9 +34,36 @@
#include "theme.h" #include "theme.h"
#include "debug_p.h" #include "debug_p.h"
uint qHash(const Plasma::SvgPrivate::CacheId &id, uint seed)
{
std::array<uint, 10> parts = {
::qHash(id.width),
::qHash(id.height),
::qHash(id.elementName),
::qHash(id.filePath),
::qHash(id.status),
::qHash(id.devicePixelRatio),
::qHash(id.scaleFactor),
::qHash(id.colorGroup),
::qHash(id.extraFlags),
::qHash(id.lastModified)
};
return qHashRange(parts.begin(), parts.end(), seed);
}
namespace Plasma namespace Plasma
{ {
class SvgRectsCacheSingleton
{
public:
SvgRectsCache self;
};
Q_GLOBAL_STATIC(SvgRectsCacheSingleton, privateSvgRectsCacheSelf)
const uint SvgRectsCache::s_seed = 0x9e3779b9;
SharedSvgRenderer::SharedSvgRenderer(QObject *parent) SharedSvgRenderer::SharedSvgRenderer(QObject *parent)
: QSvgRenderer(parent) : QSvgRenderer(parent)
@ -123,9 +150,217 @@ bool SharedSvgRenderer::load(
return true; return true;
} }
#define QLSEP QLatin1Char('_') SvgRectsCache::SvgRectsCache(QObject *parent)
#define CACHE_ID_WITH_SIZE(size, id, status, devicePixelRatio) QString::number(int(size.width())) % QLSEP % QString::number(int(size.height())) % QLSEP % id % QLSEP % QString::number(status) % QLSEP % QString::number(devicePixelRatio) : QObject(parent)
#define CACHE_ID_NATURAL_SIZE(id, status, devicePixelRatio) QLatin1String("Natural") % QLSEP % id % QLSEP % QString::number(status) % QLSEP % QString::number(devicePixelRatio) {
const QString svgElementsFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + QStringLiteral("plasma-svgelements");
m_svgElementsCache = KSharedConfig::openConfig(svgElementsFile, KConfig::SimpleConfig);
m_configSyncTimer = new QTimer(this);
m_configSyncTimer->setSingleShot(true);
m_configSyncTimer->setInterval(5000);
connect(m_configSyncTimer, &QTimer::timeout,
this, [this]() {
m_svgElementsCache->sync();
});
}
SvgRectsCache *SvgRectsCache::instance()
{
return &privateSvgRectsCacheSelf()->self;
}
void SvgRectsCache::insert(Plasma::SvgPrivate::CacheId cacheId, const QRectF &rect, unsigned int lastModified)
{
insert(qHash(cacheId, SvgRectsCache::s_seed), cacheId.filePath, rect, lastModified);
}
void SvgRectsCache::insert(uint id, const QString &filePath, const QRectF &rect, unsigned int lastModified)
{
if (m_localRectCache.contains(id)) {
return;
}
m_localRectCache.insert(id, rect);
KConfigGroup imageGroup(m_svgElementsCache, filePath);
imageGroup.writeEntry("LastModified", lastModified);
if (rect.isValid()) {
imageGroup.writeEntry(QString::number(id), rect);
} else {
m_invalidElements[filePath] << id;
imageGroup.writeEntry("Invalidelements", m_invalidElements[filePath].values());
}
m_configSyncTimer->start();
}
bool SvgRectsCache::findElementRect(Plasma::SvgPrivate::CacheId cacheId, QRectF &rect)
{
return findElementRect(qHash(cacheId, SvgRectsCache::s_seed), cacheId.filePath, rect);
}
bool SvgRectsCache::findElementRect(uint id, const QString &filePath, QRectF &rect)
{
auto it = m_localRectCache.find(id);
if (it == m_localRectCache.end()) {
if (m_invalidElements.contains(filePath) && m_invalidElements[filePath].contains(id)) {
rect = QRectF();
return true;
}
return false;
}
rect = *it;
return true;
}
void SvgRectsCache::loadImageFromCache(const QString &path, uint lastModified)
{
if (path.isEmpty()) {
return;
}
KConfigGroup imageGroup(m_svgElementsCache, path);
unsigned int savedTime = imageGroup.readEntry("LastModified", 0);
if (lastModified > savedTime) {
imageGroup.deleteGroup();
m_configSyncTimer->start();
return;
}
auto list = imageGroup.readEntry("Invalidelements", QList<unsigned int>());
m_invalidElements[path] = QSet<unsigned int>(list.begin(), list.end());
for (const auto &key : imageGroup.keyList()) {
bool ok = false;
if (ok) {
const QRectF rect = imageGroup.readEntry(key, QRectF());
m_localRectCache.insert(key.toUInt(), rect);
}
}
}
void SvgRectsCache::dropImageFromCache(const QString &path)
{
KConfigGroup imageGroup(m_svgElementsCache, path);
imageGroup.deleteGroup();
m_configSyncTimer->start();
}
QList<QSize> SvgRectsCache::sizeHintsForId(const QString &path, const QString &id)
{
const QString pathId = path % id;
if (!m_sizeHintsForId.contains(pathId)) {
KConfigGroup imageGroup(m_svgElementsCache, path);
const QStringList &encoded = imageGroup.readEntry(id, QStringList());
QList<QSize> sizes;
for (const auto &token : encoded) {
const auto &parts = token.split(QLatin1Char('x'));
if (parts.size() != 2) {
continue;
}
QSize size = QSize(parts[0].toDouble(), parts[1].toDouble());
if (!size.isEmpty()) {
sizes << size;
}
}
m_sizeHintsForId[pathId] = sizes;
return sizes;
}
return m_sizeHintsForId.value(pathId);
}
void SvgRectsCache::insertSizeHintForId(const QString &path, const QString &id, const QSize &size)
{
//TODO: need to make this more efficient
auto sizeListToString = [] (const QList<QSize> &list) {
QString ret;
for (const auto &s : list) {
ret += QString::number(s.width()) % QLatin1Char('x') % QString::number(s.height()) % QLatin1Char(',');
}
return ret;
};
m_sizeHintsForId[path % id].append(size);
KConfigGroup imageGroup(m_svgElementsCache, path);
imageGroup.writeEntry(id, sizeListToString(m_sizeHintsForId[path % id]));
m_configSyncTimer->start();
}
QString SvgRectsCache::iconThemePath()
{
if (!m_iconThemePath.isEmpty()) {
return m_iconThemePath;
}
KConfigGroup imageGroup(m_svgElementsCache, QStringLiteral("General"));
m_iconThemePath = imageGroup.readEntry(QStringLiteral("IconThemePath"), QString());
return m_iconThemePath;
}
void SvgRectsCache::setIconThemePath(const QString &path)
{
m_iconThemePath = path;
KConfigGroup imageGroup(m_svgElementsCache, QStringLiteral("General"));
imageGroup.writeEntry(QStringLiteral("IconThemePath"), path);
m_configSyncTimer->start();
}
void SvgRectsCache::expireCache(const QString &path)
{
KConfigGroup imageGroup(m_svgElementsCache, path);
unsigned int savedTime = imageGroup.readEntry("LastModified", QDateTime().toSecsSinceEpoch());
QFileInfo info(path);
if (info.exists()) {
unsigned int lastModified = info.lastModified().toSecsSinceEpoch();
if (lastModified <= savedTime) {
return;
}
}
imageGroup.deleteGroup();
}
void SvgRectsCache::setNaturalSize(const QString &path, qreal scaleFactor, const QSizeF &size)
{
KConfigGroup imageGroup(m_svgElementsCache, path);
// FIXME: needs something faster, perhaps even sprintf
imageGroup.writeEntry(QStringLiteral("NaturalSize_") % QString::number(scaleFactor), size);
m_configSyncTimer->start();
}
QSizeF SvgRectsCache::naturalSize(const QString &path, qreal scaleFactor)
{
KConfigGroup imageGroup(m_svgElementsCache, path);
// FIXME: needs something faster, perhaps even sprintf
return imageGroup.readEntry(QStringLiteral("NaturalSize_") % QString::number(scaleFactor), QSizeF());
}
QStringList SvgRectsCache::cachedKeysForPath(const QString &path) const
{
KConfigGroup imageGroup(m_svgElementsCache, path);
QStringList list = imageGroup.keyList();
QStringList filtered;
std::copy_if (list.begin(), list.end(), std::back_inserter(filtered), [](const QString element){bool ok; element.toLong(&ok); return ok;} );
return filtered;
}
void SvgRectsCache::updateLastModified(const QString &filePath, unsigned int lastModified)
{
KConfigGroup imageGroup(m_svgElementsCache, filePath);
imageGroup.writeEntry("LastModified", lastModified);
m_configSyncTimer->start();
}
SvgPrivate::SvgPrivate(Svg *svg) SvgPrivate::SvgPrivate(Svg *svg)
: q(svg), : q(svg),
@ -153,19 +388,17 @@ SvgPrivate::~SvgPrivate()
} }
//This function is meant for the rects cache //This function is meant for the rects cache
QString SvgPrivate::cacheId(const QString &elementId) const SvgPrivate::CacheId SvgPrivate::cacheId(const QString &elementId) const
{ {
if (size.isValid() && size != naturalSize) { auto idSize = size.isValid() && size != naturalSize ? size : QSizeF{-1.0, -1.0};
return CACHE_ID_WITH_SIZE(size, elementId, status, devicePixelRatio); return CacheId{idSize.width(), idSize.height(), path, elementId, status, devicePixelRatio, scaleFactor, -1, 0, lastModified};
} else {
return CACHE_ID_NATURAL_SIZE(elementId, status, devicePixelRatio);
}
} }
//This function is meant for the pixmap cache //This function is meant for the pixmap cache
QString SvgPrivate::cachePath(const QString &path, const QSize &size) const QString SvgPrivate::cachePath(const QString &id, const QSize &size) const
{ {
return CACHE_ID_WITH_SIZE(size, path, status, devicePixelRatio) % QLSEP % QString::number(colorGroup); auto cacheId = CacheId{double(size.width()), double(size.height()), path, id, status, devicePixelRatio, scaleFactor, colorGroup, 0, lastModified};
return QString::number(qHash(cacheId, SvgRectsCache::s_seed));
} }
bool SvgPrivate::setImagePath(const QString &imagePath) bool SvgPrivate::setImagePath(const QString &imagePath)
@ -207,8 +440,7 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
themed = isThemed; themed = isThemed;
path.clear(); path.clear();
themePath.clear(); themePath.clear();
localRectCache.clear();
elementsWithSizeHints.clear();
bool oldFromCurrentTheme = fromCurrentTheme; bool oldFromCurrentTheme = fromCurrentTheme;
fromCurrentTheme = !inIconTheme && isThemed && actualTheme()->currentThemeHasImage(imagePath); fromCurrentTheme = !inIconTheme && isThemed && actualTheme()->currentThemeHasImage(imagePath);
@ -234,6 +466,12 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
#endif #endif
} }
QFileInfo info(path);
lastModified = info.lastModified().toSecsSinceEpoch();
SvgRectsCache::instance()->loadImageFromCache(path, lastModified);
// check if svg wants colorscheme applied // check if svg wants colorscheme applied
checkColorHints(); checkColorHints();
@ -242,20 +480,14 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
if ((themed && !path.isEmpty() && QFileInfo::exists(path)) || QFileInfo::exists(actualPath)) { if ((themed && !path.isEmpty() && QFileInfo::exists(path)) || QFileInfo::exists(actualPath)) {
QRectF rect; QRectF rect;
if (cacheAndColorsTheme()->findInRectsCache(path, QStringLiteral("_Natural_%1").arg(scaleFactor), rect)) { naturalSize = SvgRectsCache::instance()->naturalSize(path, scaleFactor);
naturalSize = rect.size(); if (naturalSize.isEmpty()) {
} else {
createRenderer(); createRenderer();
naturalSize = renderer->defaultSize() * scaleFactor; naturalSize = renderer->defaultSize() * scaleFactor;
//qCDebug(LOG_PLASMA) << "natural size for" << path << "from renderer is" << naturalSize; SvgRectsCache::instance()->setNaturalSize(path, scaleFactor, naturalSize);
cacheAndColorsTheme()->insertIntoRectsCache(path, QStringLiteral("_Natural_%1").arg(scaleFactor), QRectF(QPointF(0, 0), naturalSize));
//qCDebug(LOG_PLASMA) << "natural size for" << path << "from cache is" << naturalSize;
} }
} }
QFileInfo info(path);
lastModified = info.lastModified().toSecsSinceEpoch();
q->resize(); q->resize();
emit q->imagePathChanged(); emit q->imagePathChanged();
@ -291,39 +523,15 @@ QPixmap SvgPrivate::findInCache(const QString &elementId, qreal ratio, const QSi
QSize size; QSize size;
QString actualElementId; QString actualElementId;
if (elementsWithSizeHints.isEmpty()) {
// Fetch all size hinted element ids from the theme's rect cache
// and store them locally.
const QRegularExpression sizeHintedKeyExpr(QLatin1String("^") + CACHE_ID_NATURAL_SIZE(QStringLiteral("(\\d+)-(\\d+)-(.+)"), status, ratio) + QLatin1String("$"));
const auto lst = cacheAndColorsTheme()->listCachedRectKeys(path);
for (const QString &key : lst) {
const auto match = sizeHintedKeyExpr.match(key);
if (match.hasMatch()) {
QString baseElementId = match.captured(3);
QSize sizeHint(match.capturedRef(1).toInt(),
match.capturedRef(2).toInt());
if (sizeHint.isValid()) {
elementsWithSizeHints.insert(baseElementId, sizeHint);
}
}
}
if (elementsWithSizeHints.isEmpty()) {
// Make sure we won't query the theme unnecessarily.
elementsWithSizeHints.insert(QString(), QSize());
}
}
// Look at the size hinted elements and try to find the smallest one with an // Look at the size hinted elements and try to find the smallest one with an
// identical aspect ratio. // identical aspect ratio.
if (s.isValid() && !elementId.isEmpty()) { if (s.isValid() && !elementId.isEmpty()) {
const QList<QSize> elementSizeHints = elementsWithSizeHints.values(elementId); const QList<QSize> elementSizeHints = SvgRectsCache::instance()->sizeHintsForId(path, elementId);
if (!elementSizeHints.isEmpty()) { if (!elementSizeHints.isEmpty()) {
QSize bestFit(-1, -1); QSizeF bestFit(-1, -1);
for (const QSize &hint : elementSizeHints) { for (const auto &hint : elementSizeHints) {
if (hint.width() >= s.width() * ratio && hint.height() >= s.height() * ratio && if (hint.width() >= s.width() * ratio && hint.height() >= s.height() * ratio &&
(!bestFit.isValid() || (!bestFit.isValid() ||
@ -353,9 +561,7 @@ QPixmap SvgPrivate::findInCache(const QString &elementId, qreal ratio, const QSi
return QPixmap(); return QPixmap();
} }
const QString id = cachePath(path, size) + actualElementId; const QString id = cachePath(actualElementId, size);
//qCDebug(LOG_PLASMA) << "id is " << id;
QPixmap p; QPixmap p;
if (cacheRendering && cacheAndColorsTheme()->findInCache(id, p, lastModified)) { if (cacheRendering && cacheAndColorsTheme()->findInCache(id, p, lastModified)) {
@ -364,11 +570,6 @@ QPixmap SvgPrivate::findInCache(const QString &elementId, qreal ratio, const QSi
return p; return p;
} }
//qCDebug(LOG_PLASMA) << "didn't find cached version of " << id << ", so re-rendering";
//qCDebug(LOG_PLASMA) << "size for " << actualElementId << " is " << s;
// we have to re-render this puppy
createRenderer(); createRenderer();
QRectF finalRect = makeUniform(renderer->boundsOnElement(actualElementId), QRect(QPoint(0, 0), size)); QRectF finalRect = makeUniform(renderer->boundsOnElement(actualElementId), QRect(QPoint(0, 0), size));
@ -397,9 +598,11 @@ QPixmap SvgPrivate::findInCache(const QString &elementId, qreal ratio, const QSi
} }
if (cacheRendering) { if (cacheRendering) {
cacheAndColorsTheme()->insertIntoCache(id, p, QString::number((qint64)q, 16) % QLSEP % actualElementId); cacheAndColorsTheme()->insertIntoCache(id, p, QString::number((qint64)q, 16) % QLatin1Char('_') % actualElementId);
} }
SvgRectsCache::instance()->updateLastModified(path, lastModified);
return p; return p;
} }
@ -409,7 +612,6 @@ void SvgPrivate::createRenderer()
return; return;
} }
//qCDebug(LOG_PLASMA) << kBacktrace();
if (themed && path.isEmpty() && !themeFailed) { if (themed && path.isEmpty() && !themeFailed) {
Applet *applet = qobject_cast<Applet *>(q->parent()); Applet *applet = qobject_cast<Applet *>(q->parent());
//FIXME: this maybe could be more efficient if we knew if the package was empty, e.g. for //FIXME: this maybe could be more efficient if we knew if the package was empty, e.g. for
@ -433,17 +635,12 @@ void SvgPrivate::createRenderer()
} }
} }
//qCDebug(LOG_PLASMA) << "********************************";
//qCDebug(LOG_PLASMA) << "FAIL! **************************";
//qCDebug(LOG_PLASMA) << path << "**";
QString styleSheet = cacheAndColorsTheme()->d->svgStyleSheet(colorGroup, status); QString styleSheet = cacheAndColorsTheme()->d->svgStyleSheet(colorGroup, status);
styleCrc = qChecksum(styleSheet.toUtf8().constData(), styleSheet.size()); styleCrc = qChecksum(styleSheet.toUtf8().constData(), styleSheet.size());
QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(styleCrc + path); QHash<QString, SharedSvgRenderer::Ptr>::const_iterator it = s_renderers.constFind(styleCrc + path);
if (it != s_renderers.constEnd()) { if (it != s_renderers.constEnd()) {
//qCDebug(LOG_PLASMA) << "gots us an existing one!";
renderer = it.value(); renderer = it.value();
} else { } else {
if (path.isEmpty()) { if (path.isEmpty()) {
@ -455,14 +652,19 @@ void SvgPrivate::createRenderer()
// Add interesting elements to the theme's rect cache. // Add interesting elements to the theme's rect cache.
QHashIterator<QString, QRectF> i(interestingElements); QHashIterator<QString, QRectF> i(interestingElements);
QRegularExpression sizeHintedKeyExpr(QStringLiteral("^(\\d+)-(\\d+)-(.+)$"));
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
const QString &elementId = i.key(); const QString &elementId = i.key();
QString originalId = i.key();
const QRectF &elementRect = i.value(); const QRectF &elementRect = i.value();
const QString cacheId = CACHE_ID_NATURAL_SIZE(elementId, status, devicePixelRatio); originalId.replace(sizeHintedKeyExpr, QStringLiteral("\\3"));
localRectCache.insert(cacheId, elementRect); SvgRectsCache::instance()->insertSizeHintForId(path, originalId, elementRect.size().toSize());
cacheAndColorsTheme()->insertIntoRectsCache(path, cacheId, elementRect);
const CacheId cacheId({-1.0, -1.0, path, elementId, status, devicePixelRatio, scaleFactor, -1, 0, lastModified});
SvgRectsCache::instance()->insert(cacheId, elementRect, lastModified);
} }
} }
@ -484,16 +686,10 @@ void SvgPrivate::eraseRenderer()
#endif #endif
// this and the cache reference it // this and the cache reference it
s_renderers.erase(s_renderers.find(styleCrc + path)); s_renderers.erase(s_renderers.find(styleCrc + path));
if (theme) {
theme.data()->releaseRectsCache(path);
}
} }
renderer = nullptr; renderer = nullptr;
styleCrc = 0; styleCrc = 0;
localRectCache.clear();
elementsWithSizeHints.clear();
} }
QRectF SvgPrivate::elementRect(const QString &elementId) QRectF SvgPrivate::elementRect(const QString &elementId)
@ -515,35 +711,23 @@ QRectF SvgPrivate::elementRect(const QString &elementId)
return QRectF(); return QRectF();
} }
const QString id = cacheId(elementId);
const auto it = localRectCache.constFind(id);
if (it != localRectCache.constEnd()) {
return *it;
}
QRectF rect; QRectF rect;
bool found = cacheAndColorsTheme()->findInRectsCache(path, id, rect); const CacheId cacheId = SvgPrivate::cacheId(elementId);
bool found = SvgRectsCache::instance()->findElementRect(cacheId, rect);
//This is a corner case where we are *sure* the element is not valid //This is a corner case where we are *sure* the element is not valid
if (found && rect == QRectF()) { if (!found) {
return rect; rect = findAndCacheElementRect(elementId);
} else if (found) {
localRectCache.insert(id, rect);
} else {
rect = findAndCacheElementRect(elementId, id);
} }
return rect; return rect;
} }
QRectF SvgPrivate::findAndCacheElementRect(const QString &elementId, const QString &id) QRectF SvgPrivate::findAndCacheElementRect(const QString &elementId)
{ {
//we need to check the id before createRenderer(), otherwise it may generate a different id compared to the previous cacheId)( call //we need to check the id before createRenderer(), otherwise it may generate a different id compared to the previous cacheId)( call
createRenderer(); const CacheId cacheId = SvgPrivate::cacheId(elementId);
const auto it = localRectCache.constFind(id); createRenderer();
if (it != localRectCache.constEnd()) {
return *it;
}
//This code will usually never be run because createRenderer already caches all the boundingRect in the elements in the svg //This code will usually never be run because createRenderer already caches all the boundingRect in the elements in the svg
QRectF elementRect = renderer->elementExists(elementId) ? QRectF elementRect = renderer->elementExists(elementId) ?
@ -560,9 +744,7 @@ QRectF SvgPrivate::findAndCacheElementRect(const QString &elementId, const QStri
elementRect = QRectF(elementRect.x() * dx, elementRect.y() * dy, elementRect = QRectF(elementRect.x() * dx, elementRect.y() * dy,
elementRect.width() * dx, elementRect.height() * dy); elementRect.width() * dx, elementRect.height() * dy);
SvgRectsCache::instance()->insert(cacheId, elementRect, lastModified);
cacheAndColorsTheme()->insertIntoRectsCache(path, id, elementRect);
localRectCache.insert(id, elementRect);
return elementRect; return elementRect;
} }
@ -642,7 +824,6 @@ QRectF SvgPrivate::makeUniform(const QRectF &orig, const QRectF &dst)
res.setHeight(res.height() + offset); res.setHeight(res.height() + offset);
} }
//qCDebug(LOG_PLASMA)<<"Aligning Rects, origin:"<<orig<<"destination:"<<dst<<"result:"<<res;
return res; return res;
} }
@ -732,9 +913,8 @@ void Svg::setScaleFactor(qreal ratio)
//not resize() because we want to do it unconditionally //not resize() because we want to do it unconditionally
QRectF rect; QRectF rect;
if (d->cacheAndColorsTheme()->findInRectsCache(d->path, QStringLiteral("_Natural_%1").arg(d->scaleFactor), rect)) { d->naturalSize = SvgRectsCache::instance()->naturalSize(d->path, d->scaleFactor);
d->naturalSize = rect.size(); if (d->naturalSize.isEmpty()) {
} else {
d->createRenderer(); d->createRenderer();
d->naturalSize = d->renderer->defaultSize() * d->scaleFactor; d->naturalSize = d->renderer->defaultSize() * d->scaleFactor;
} }
@ -840,7 +1020,6 @@ void Svg::resize(const QSizeF &size)
} }
d->size = size; d->size = size;
d->localRectCache.clear();
emit sizeChanged(); emit sizeChanged();
} }
@ -852,7 +1031,6 @@ void Svg::resize()
} }
d->size = d->naturalSize; d->size = d->naturalSize;
d->localRectCache.clear();
emit sizeChanged(); emit sizeChanged();
} }
@ -883,14 +1061,14 @@ bool Svg::isValid() const
//try very hard to avoid creation of a parser //try very hard to avoid creation of a parser
QRectF rect; QRectF rect;
if (d->cacheAndColorsTheme()->findInRectsCache(d->path, QStringLiteral("_Natural_%1").arg(d->scaleFactor), rect)) { QSizeF naturalSize = SvgRectsCache::instance()->naturalSize(d->path, d->scaleFactor);
if (!naturalSize.isEmpty()) {
return true; return true;
} }
if (d->path.isEmpty() || !QFileInfo::exists(d->path)) { if (d->path.isEmpty() || !QFileInfo::exists(d->path)) {
return false; return false;
} }
d->createRenderer(); d->createRenderer();
return d->renderer->isValid(); return d->renderer->isValid();
} }
@ -908,7 +1086,6 @@ bool Svg::containsMultipleImages() const
void Svg::setImagePath(const QString &svgFilePath) void Svg::setImagePath(const QString &svgFilePath)
{ {
if (d->setImagePath(svgFilePath)) { if (d->setImagePath(svgFilePath)) {
//qCDebug(LOG_PLASMA) << "repaintNeeded";
emit repaintNeeded(); emit repaintNeeded();
} }
} }

View File

@ -6,6 +6,7 @@
#include "theme.h" #include "theme.h"
#include "private/theme_p.h" #include "private/theme_p.h"
#include "private/svg_p.h"
#include <QFile> #include <QFile>
#include <QFontDatabase> #include <QFontDatabase>
@ -38,11 +39,11 @@ Theme::Theme(QObject *parent)
{ {
if (!ThemePrivate::globalTheme) { if (!ThemePrivate::globalTheme) {
ThemePrivate::globalTheme = new ThemePrivate; ThemePrivate::globalTheme = new ThemePrivate;
ThemePrivate::globalTheme->settingsChanged(false);
} }
ThemePrivate::globalTheme->ref.ref(); ThemePrivate::globalTheme->ref.ref();
d = ThemePrivate::globalTheme; d = ThemePrivate::globalTheme;
d->settingsChanged(false);
if (QCoreApplication::instance()) { if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
d, &ThemePrivate::onAppExitCleanup); d, &ThemePrivate::onAppExitCleanup);
@ -335,36 +336,13 @@ bool Theme::findInRectsCache(const QString &image, const QString &element, QRect
return false; return false;
} }
KConfigGroup imageGroup(d->svgElementsCache, image); bool ok = false;
rect = imageGroup.readEntry(element % QLatin1String("Size"), QRectF()); uint id = element.toLong(&ok);
if (!ok) {
if (rect.isValid()) {
return true;
}
//Name starting by _ means the element is empty and we're asked for the size of
//the whole image, so the whole image is never invalid
if (element.indexOf(QLatin1Char('_')) <= 0) {
return false; return false;
} }
bool invalid = false; return SvgRectsCache::instance()->findElementRect(id, image, rect);
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
if (it == d->invalidElements.end()) {
const QStringList elementList = imageGroup.readEntry("invalidElements", QStringList());
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
const QSet<QString> elements(elementList.begin(), elementList.end());
#else
const QSet<QString> elements = elementList.toSet();
#endif
d->invalidElements.insert(image, elements);
invalid = elements.contains(element);
} else {
invalid = it.value().contains(element);
}
return invalid;
} }
QStringList Theme::listCachedRectKeys(const QString &image) const QStringList Theme::listCachedRectKeys(const QString &image) const
@ -373,21 +351,7 @@ QStringList Theme::listCachedRectKeys(const QString &image) const
return QStringList(); return QStringList();
} }
KConfigGroup imageGroup(d->svgElementsCache, image); return SvgRectsCache::instance()->cachedKeysForPath(image);
QStringList keys = imageGroup.keyList();
QMutableListIterator<QString> i(keys);
while (i.hasNext()) {
QString key = i.next();
if (key.endsWith(QLatin1String("Size"))) {
// The actual cache id used from outside doesn't end on "Size".
key.resize(key.size() - 4);
i.setValue(key);
} else {
i.remove();
}
}
return keys;
} }
void Theme::insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect) void Theme::insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect)
@ -396,46 +360,26 @@ void Theme::insertIntoRectsCache(const QString &image, const QString &element, c
return; return;
} }
if (rect.isValid()) { bool ok = false;
KConfigGroup imageGroup(d->svgElementsCache, image); uint id = element.toLong(&ok);
imageGroup.writeEntry(element % QLatin1String("Size"), rect); if (!ok) {
} else { return;
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
if (it == d->invalidElements.end()) {
d->invalidElements[image].insert(element);
} else if (!it.value().contains(element)) {
if (it.value().count() > 1000) {
it.value().erase(it.value().begin());
} }
it.value().insert(element); uint secs = QDateTime::currentSecsSinceEpoch();
} SvgRectsCache::instance()->insert(id, image, rect, secs);
}
QMetaObject::invokeMethod(d->rectSaveTimer, "start");
} }
void Theme::invalidateRectsCache(const QString &image) void Theme::invalidateRectsCache(const QString &image)
{ {
if (d->useCache()) {
KConfigGroup imageGroup(d->svgElementsCache, image);
imageGroup.deleteGroup();
}
d->invalidElements.remove(image); SvgRectsCache::instance()->dropImageFromCache(image);
} }
void Theme::releaseRectsCache(const QString &image) void Theme::releaseRectsCache(const QString &image)
{ {
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image); Q_UNUSED(image);
if (it != d->invalidElements.end()) { // No op: the internal svg cache always writes the invalid elements in the proper place
if (d->useCache()) {
KConfigGroup imageGroup(d->svgElementsCache, it.key());
imageGroup.writeEntry("invalidElements", it.value().values());
}
d->invalidElements.erase(it);
}
} }
void Theme::setCacheLimit(int kbytes) void Theme::setCacheLimit(int kbytes)

View File

@ -270,6 +270,7 @@ public:
**/ **/
void setCacheLimit(int kbytes); void setCacheLimit(int kbytes);
#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 78)
/** /**
* Tries to load the rect of a sub element from a disk cache * Tries to load the rect of a sub element from a disk cache
* *
@ -279,6 +280,7 @@ public:
* if not found or if we are sure it doesn't exist it will be QRect() * if not found or if we are sure it doesn't exist it will be QRect()
* @return true if the element was found in cache or if we are sure the element doesn't exist * @return true if the element was found in cache or if we are sure the element doesn't exist
**/ **/
PLASMA_DEPRECATED_VERSION(5, 78, "Rects Cache public API is deprecated")
bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const; bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const;
/** /**
@ -290,6 +292,7 @@ public:
* *
* @since 4.6 * @since 4.6
*/ */
PLASMA_DEPRECATED_VERSION(5, 78, "Rects Cache public API is deprecated")
QStringList listCachedRectKeys(const QString &image) const; QStringList listCachedRectKeys(const QString &image) const;
/** /**
@ -299,6 +302,7 @@ public:
* @param element sub element we want insert the rect * @param element sub element we want insert the rect
* @param rect element rectangle * @param rect element rectangle
**/ **/
PLASMA_DEPRECATED_VERSION(5, 78, "Rects Cache public API is deprecated")
void insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect); void insertIntoRectsCache(const QString &image, const QString &element, const QRectF &rect);
/** /**
@ -306,6 +310,7 @@ public:
* *
* @param image the path to the image the cache is associated with * @param image the path to the image the cache is associated with
**/ **/
PLASMA_DEPRECATED_VERSION(5, 78, "Rects Cache public API is deprecated")
void invalidateRectsCache(const QString &image); void invalidateRectsCache(const QString &image);
/** /**
@ -315,7 +320,9 @@ public:
* *
* @param image the path to the image the cache is associated with * @param image the path to the image the cache is associated with
*/ */
PLASMA_DEPRECATED_VERSION(5, 78, "Rects Cache public API is deprecated")
void releaseRectsCache(const QString &image); void releaseRectsCache(const QString &image);
#endif
#if PLASMA_ENABLE_DEPRECATED_SINCE(5, 67) #if PLASMA_ENABLE_DEPRECATED_SINCE(5, 67)
/** /**

View File

@ -46,6 +46,7 @@ target_link_libraries(KF5PlasmaQuick
KF5::Plasma KF5::Plasma
KF5::WindowSystem KF5::WindowSystem
PRIVATE PRIVATE
Qt5::Svg
KF5::KIOWidgets KF5::KIOWidgets
KF5::I18n KF5::I18n
KF5::IconThemes KF5::IconThemes