Do not require an exact match for size hinted elements, but search for the smallest size hinted element that is bigger than the display size. Thanks to mommertz, who outlined and mentored this patch, element ids are cached both locally and in the theme, so no additional calls to createRenderer are required.

This was discussed in <http://svn.reviewboard.kde.org/r/5689/>.

svn path=/trunk/KDE/kdelibs/; revision=1191192
This commit is contained in:
Ingomar Wesp 2010-10-29 22:14:18 +00:00
parent c251a29564
commit e049ecb34d
4 changed files with 170 additions and 12 deletions

View File

@ -37,11 +37,23 @@ class SharedSvgRenderer : public QSvgRenderer, public QSharedData
typedef KSharedPtr<SharedSvgRenderer> Ptr;
SharedSvgRenderer(QObject *parent = 0);
SharedSvgRenderer(const QString &filename, const QString &styleSheet, QObject *parent = 0);
SharedSvgRenderer(const QByteArray &contents, const QString &styleSheet, QObject *parent = 0);
SharedSvgRenderer(
const QString &filename,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints,
QObject *parent = 0);
SharedSvgRenderer(
const QByteArray &contents,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints,
QObject *parent = 0);
private:
bool load(const QByteArray &contents, const QString &styleSheet);
bool load(
const QByteArray &contents,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints);
};
class SvgPrivate
@ -86,6 +98,7 @@ public:
Svg *q;
QWeakPointer<Theme> theme;
QHash<QString, QRectF> localRectCache;
QHash<QString, QSize> elementsWithSizeHints;
SharedSvgRenderer::Ptr renderer;
QString themePath;
QString path;

131
svg.cpp
View File

@ -49,7 +49,11 @@ SharedSvgRenderer::SharedSvgRenderer(QObject *parent)
{
}
SharedSvgRenderer::SharedSvgRenderer(const QString &filename, const QString &styleSheet, QObject *parent)
SharedSvgRenderer::SharedSvgRenderer(
const QString &filename,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints,
QObject *parent)
: QSvgRenderer(parent)
{
QIODevice *file = KFilterDev::deviceForFile(filename, "application/x-gzip");
@ -57,25 +61,53 @@ SharedSvgRenderer::SharedSvgRenderer(const QString &filename, const QString &sty
delete file;
return;
}
load(file->readAll(), styleSheet);
load(file->readAll(), styleSheet, elementsWithSizeHints);
delete file;
}
SharedSvgRenderer::SharedSvgRenderer(const QByteArray &contents, const QString &styleSheet, QObject *parent)
SharedSvgRenderer::SharedSvgRenderer(
const QByteArray &contents,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints,
QObject *parent)
: QSvgRenderer(parent)
{
load(contents, styleSheet);
load(contents, styleSheet, elementsWithSizeHints);
}
bool SharedSvgRenderer::load(const QByteArray &contents, const QString &styleSheet)
bool SharedSvgRenderer::load(
const QByteArray &contents,
const QString &styleSheet,
QHash<QString, QSize> &elementsWithSizeHints)
{
{ // Search the SVG to find and store all ids that contain size hints.
const QString contentsAsString(QString::fromLatin1(contents));
QRegExp idExpr("id\\s*=\\s*(['\"])(\\d+)-(\\d+)-(.+)\\1");
idExpr.setMinimal(true);
int pos = 0;
while ((pos = idExpr.indexIn(contentsAsString, pos)) != -1) {
QString elementId = idExpr.cap(4);
QSize sizeHint(idExpr.cap(2).toInt(), idExpr.cap(3).toInt());
if (sizeHint.isValid()) {
elementsWithSizeHints.insertMulti(elementId, sizeHint);
}
pos += idExpr.matchedLength();
}
}
// Apply the style sheet.
if (styleSheet.isEmpty() || ! contents.contains("current-color-scheme")) {
return QSvgRenderer::load(contents);
}
QDomDocument svg;
if (!svg.setContent(contents)) {
return false;
}
QDomNode defs = svg.elementsByTagName("defs").item(0);
for (QDomElement style = defs.firstChildElement("style"); !style.isNull();
@ -96,6 +128,8 @@ bool SharedSvgRenderer::load(const QByteArray &contents, const QString &styleShe
#define QLSEP QLatin1Char('_')
#define CACHE_ID_WITH_SIZE(size, id) QString::number(int(size.width())) % QString::number(int(size.height())) % QLSEP % id
#define CACHE_ID_NATURAL_SIZE(id) QLatin1Literal("Natural") % QLSEP % id
SvgPrivate::SvgPrivate(Svg *svg)
: q(svg),
renderer(0),
@ -121,7 +155,7 @@ QString SvgPrivate::cacheId(const QString &elementId)
if (size.isValid() && size != naturalSize) {
return CACHE_ID_WITH_SIZE(size, elementId);
} else {
return QLatin1Literal("Natural") % QLSEP % elementId;
return CACHE_ID_NATURAL_SIZE(elementId);
}
}
@ -157,6 +191,7 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
path.clear();
themePath.clear();
localRectCache.clear();
elementsWithSizeHints.clear();
if (themed) {
themePath = imagePath;
@ -216,7 +251,60 @@ Theme *SvgPrivate::actualTheme()
QPixmap SvgPrivate::findInCache(const QString &elementId, const QSizeF &s)
{
QSize size;
QString actualElementId(QString::number(int(s.width())) % "-" % QString::number(int(s.height())) % "-" % elementId);
QString actualElementId;
if (elementsWithSizeHints.isEmpty()) {
// Fetch all size hinted element ids from the theme's rect cache
// and store them locally.
QRegExp sizeHintedKeyExpr(CACHE_ID_NATURAL_SIZE("(\\d+)-(\\d+)-(.+)"));
Q_FOREACH(const QString &key, actualTheme()->listCachedRectKeys(path)) {
if (sizeHintedKeyExpr.exactMatch(key)) {
QString baseElementId = sizeHintedKeyExpr.cap(3);
QSize sizeHint(
sizeHintedKeyExpr.cap(1).toInt(),
sizeHintedKeyExpr.cap(2).toInt());
if (sizeHint.isValid()) {
elementsWithSizeHints.insertMulti(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
// identical aspect ratio.
if (s.isValid() && !elementId.isEmpty()) {
QList<QSize> elementSizeHints = elementsWithSizeHints.values(elementId);
if (!elementSizeHints.isEmpty()) {
QSize bestFit(-1, -1);
Q_FOREACH(const QSize &hint, elementSizeHints) {
if (hint.width() >= s.width() && hint.height() >= s.height() &&
(!bestFit.isValid() ||
bestFit.width() * bestFit.height() >
hint.width() * hint.height())) {
bestFit = hint;
}
}
if (bestFit.isValid()) {
actualElementId =
QString::number(bestFit.width()) % "-" %
QString::number(bestFit.height()) % "-" % elementId;
}
}
}
if (elementId.isEmpty() || !q->hasElement(actualElementId)) {
actualElementId = elementId;
@ -329,7 +417,33 @@ void SvgPrivate::createRenderer()
if (path.isEmpty()) {
renderer = new SharedSvgRenderer();
} else {
renderer = new SharedSvgRenderer(path, actualTheme()->styleSheet("SVG"));
renderer = new SharedSvgRenderer(
path, actualTheme()->styleSheet("SVG"), elementsWithSizeHints);
// Add size hinted elements to the theme's rect cache.
QHashIterator<QString, QSize> i(elementsWithSizeHints);
while (i.hasNext()) {
i.next();
const QString &baseElementId = i.key();
const QSize &hintedSize = i.value();
QString fullElementId =
QString::number(hintedSize.width()) % '-' %
QString::number(hintedSize.height()) % '-' %
baseElementId;
QString cacheId = CACHE_ID_NATURAL_SIZE(fullElementId);
QRectF elementRect = renderer->boundsOnElement(fullElementId);
if (elementRect.isValid()) {
actualTheme()->insertIntoRectsCache(
path, cacheId, elementRect);
}
}
}
if (elementsWithSizeHints.isEmpty()) {
// Make sure we won't query the theme unnecessarily.
elementsWithSizeHints.insert(QString(), QSize());
}
s_renderers[styleCrc + path] = renderer;
@ -354,6 +468,7 @@ void SvgPrivate::eraseRenderer()
renderer = 0;
styleCrc = 0;
localRectCache.clear();
elementsWithSizeHints.clear();
}
QRectF SvgPrivate::elementRect(const QString &elementId)

View File

@ -22,6 +22,7 @@
#include <QApplication>
#include <QFile>
#include <QFileInfo>
#include <QMutableListIterator>
#include <QPair>
#include <QStringBuilder>
#include <QTimer>
@ -960,6 +961,24 @@ bool Theme::findInRectsCache(const QString &image, const QString &element, QRect
return invalid;
}
QStringList Theme::listCachedRectKeys(const QString &image) const
{
KConfigGroup imageGroup(d->svgElementsCache, image);
QStringList keys = imageGroup.keyList();
QMutableListIterator<QString> i(keys);
while (i.hasNext()) {
const QString &key = i.next();
if (key.endsWith("Size")) {
// The actual cache id used from outside doesn't end on "Size".
i.setValue(key.resize(key.size() - 4));
} else {
i.remove();
}
}
return keys;
}
void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
{
if (!d->pixmapCache) {

13
theme.h
View File

@ -330,12 +330,23 @@ class PLASMA_EXPORT Theme : public QObject
*
* @arg image path of the image we want to check
* @arg element sub element we want to retrieve
* @arg rect output parameter of the element rect found in cache
* @arg rect output parameter of the element rect found in cache
* 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
**/
bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const;
/**
* Returns a list of all keys of cached rects for the given image.
*
* @arg image path of the image for which the keys should be returned
*
* @return a QStringList whose elements are the entry keys in the rects cache
*
* @since 4.6
*/
QStringList listCachedRectKeys(const QString &image) const;
/**
* Inserts a rectangle of a sub element of an image into a disk cache
*