File indexing completed on 2024-05-12 05:46:44

0001 /*
0002  *   Copyright 2008-2010 by Aaron Seigo <aseigo@kde.org>
0003  *   Copyright 2008-2010 Marco Martin <notmart@gmail.com>
0004  *
0005  *   This program is free software; you can redistribute it and/or modify
0006  *   it under the terms of the GNU Library General Public License as
0007  *   published by the Free Software Foundation; either version 2, or
0008  *   (at your option) any later version.
0009  *
0010  *   This program is distributed in the hope that it will be useful,
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  *   GNU General Public License for more details
0014  *
0015  *   You should have received a copy of the GNU Library General Public
0016  *   License along with this program; if not, write to the
0017  *   Free Software Foundation, Inc.,
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0019  */
0020 
0021 #include "framesvg.h"
0022 #include "private/framesvg_p.h"
0023 
0024 #include <QAtomicInt>
0025 #include <QBitmap>
0026 #include <QCryptographicHash>
0027 #include <QPainter>
0028 #include <QRegion>
0029 #include <QSize>
0030 #include <QStringBuilder>
0031 #include <QTimer>
0032 
0033 #include <QDebug>
0034 
0035 #include "theme.h"
0036 #include "private/svg_p.h"
0037 #include "private/framesvg_helpers.h"
0038 #include "debug_p.h"
0039 
0040 namespace Plasma
0041 {
0042 
0043 QHash<ThemePrivate *, QHash<QString, QWeakPointer<FrameData>> > FrameSvgPrivate::s_sharedFrames;
0044 
0045 // Any attempt to generate a frame whose width or height is larger than this
0046 // will be rejected
0047 static const int MAX_FRAME_SIZE = 100000;
0048 
0049 FrameData::~FrameData()
0050 {
0051     FrameSvgPrivate::s_sharedFrames[theme].remove(cacheId);
0052 }
0053 
0054 FrameSvg::FrameSvg(QObject *parent)
0055     : Svg(parent),
0056       d(new FrameSvgPrivate(this))
0057 {
0058     connect(this, &FrameSvg::repaintNeeded, this, std::bind(&FrameSvgPrivate::updateNeeded, d));
0059 }
0060 
0061 FrameSvg::~FrameSvg()
0062 {
0063     delete d;
0064 }
0065 
0066 void FrameSvg::setImagePath(const QString &path)
0067 {
0068     if (path == imagePath()) {
0069         return;
0070     }
0071 
0072     clearCache();
0073 
0074     setContainsMultipleImages(true);
0075     Svg::d->setImagePath(path);
0076     if (!d->repaintBlocked) {
0077         d->updateFrameData();
0078     }
0079 }
0080 
0081 void FrameSvg::setEnabledBorders(const EnabledBorders borders)
0082 {
0083     if (borders == d->enabledBorders) {
0084         return;
0085     }
0086 
0087     d->enabledBorders = borders;
0088 
0089     if (!d->repaintBlocked) {
0090         d->updateFrameData();
0091     }
0092 }
0093 
0094 FrameSvg::EnabledBorders FrameSvg::enabledBorders() const
0095 {
0096     return d->enabledBorders;
0097 }
0098 
0099 void FrameSvg::setElementPrefix(Plasma::Types::Location location)
0100 {
0101     switch (location) {
0102     case Types::TopEdge:
0103         setElementPrefix(QStringLiteral("north"));
0104         break;
0105     case Types::BottomEdge:
0106         setElementPrefix(QStringLiteral("south"));
0107         break;
0108     case Types::LeftEdge:
0109         setElementPrefix(QStringLiteral("west"));
0110         break;
0111     case Types::RightEdge:
0112         setElementPrefix(QStringLiteral("east"));
0113         break;
0114     default:
0115         setElementPrefix(QString());
0116         break;
0117     }
0118 
0119     d->location = location;
0120 }
0121 
0122 void FrameSvg::setElementPrefix(const QString &prefix)
0123 {
0124     if (!hasElement(prefix % QLatin1String("-center"))) {
0125         d->prefix.clear();
0126     } else {
0127         d->prefix = prefix;
0128         if (!d->prefix.isEmpty()) {
0129             d->prefix += QLatin1Char('-');
0130         }
0131     }
0132     d->requestedPrefix = prefix;
0133 
0134     d->location = Types::Floating;
0135 
0136     if (!d->repaintBlocked) {
0137         d->updateFrameData();
0138     }
0139 }
0140 
0141 bool FrameSvg::hasElementPrefix(const QString &prefix) const
0142 {
0143     //for now it simply checks if a center element exists,
0144     //because it could make sense for certain themes to not have all the elements
0145     if (prefix.isEmpty()) {
0146         return hasElement(QStringLiteral("center"));
0147     }
0148     if (prefix.endsWith(QLatin1Char('-'))) {
0149         return hasElement(prefix % QLatin1String("center"));
0150     }
0151 
0152     return hasElement(prefix % QLatin1String("-center"));
0153 }
0154 
0155 bool FrameSvg::hasElementPrefix(Plasma::Types::Location location) const
0156 {
0157     switch (location) {
0158     case Types::TopEdge:
0159         return hasElementPrefix(QStringLiteral("north"));
0160     case Types::BottomEdge:
0161         return hasElementPrefix(QStringLiteral("south"));
0162     case Types::LeftEdge:
0163         return hasElementPrefix(QStringLiteral("west"));
0164     case Types::RightEdge:
0165         return hasElementPrefix(QStringLiteral("east"));
0166     default:
0167         return hasElementPrefix(QString());
0168     }
0169 }
0170 
0171 QString FrameSvg::prefix()
0172 {
0173     return d->requestedPrefix;
0174 }
0175 
0176 void FrameSvg::resizeFrame(const QSizeF &size)
0177 {
0178     if (imagePath().isEmpty()) {
0179         return;
0180     }
0181 
0182     if (size.isEmpty()) {
0183 #ifndef NDEBUG
0184         // qCDebug(LOG_PLASMA) << "Invalid size" << size;
0185 #endif
0186         return;
0187     }
0188 
0189     if (d->frame && size.toSize() == d->frame->frameSize) {
0190         return;
0191     }
0192     d->pendingFrameSize = size.toSize();
0193 
0194     if (!d->repaintBlocked) {
0195         d->updateFrameData(FrameSvgPrivate::UpdateFrame);
0196     }
0197 }
0198 
0199 QSizeF FrameSvg::frameSize() const
0200 {
0201     if (!d->frame) {
0202         return QSize(-1, -1);
0203     } else {
0204         return d->frameSize(d->frame.data());
0205     }
0206 }
0207 
0208 qreal FrameSvg::marginSize(const Plasma::Types::MarginEdge edge) const
0209 {
0210     if (!d->frame) {
0211         return .0;
0212     }
0213 
0214     if (d->frame->noBorderPadding) {
0215         return .0;
0216     }
0217 
0218     switch (edge) {
0219     case Plasma::Types::TopMargin:
0220         return d->frame->topMargin;
0221 
0222     case Plasma::Types::LeftMargin:
0223         return d->frame->leftMargin;
0224 
0225     case Plasma::Types::RightMargin:
0226         return d->frame->rightMargin;
0227 
0228     //Plasma::BottomMargin
0229     default:
0230         return d->frame->bottomMargin;
0231     }
0232 }
0233 
0234 qreal FrameSvg::fixedMarginSize(const Plasma::Types::MarginEdge edge) const
0235 {
0236     if (!d->frame) {
0237         return .0;
0238     }
0239 
0240     if (d->frame->noBorderPadding) {
0241         return .0;
0242     }
0243 
0244     switch (edge) {
0245     case Plasma::Types::TopMargin:
0246         return d->frame->fixedTopMargin;
0247 
0248     case Plasma::Types::LeftMargin:
0249         return d->frame->fixedLeftMargin;
0250 
0251     case Plasma::Types::RightMargin:
0252         return d->frame->fixedRightMargin;
0253 
0254     //Plasma::BottomMargin
0255     default:
0256         return d->frame->fixedBottomMargin;
0257     }
0258 }
0259 
0260 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
0261 {
0262     if (!d->frame || d->frame->noBorderPadding) {
0263         left = top = right = bottom = 0;
0264         return;
0265     }
0266 
0267     top = d->frame->topMargin;
0268     left = d->frame->leftMargin;
0269     right = d->frame->rightMargin;
0270     bottom = d->frame->bottomMargin;
0271 }
0272 
0273 void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
0274 {
0275     if (!d->frame || d->frame->noBorderPadding) {
0276         left = top = right = bottom = 0;
0277         return;
0278     }
0279 
0280     top = d->frame->fixedTopMargin;
0281     left = d->frame->fixedLeftMargin;
0282     right = d->frame->fixedRightMargin;
0283     bottom = d->frame->fixedBottomMargin;
0284 }
0285 
0286 QRectF FrameSvg::contentsRect() const
0287 {
0288     if (d->frame) {
0289         QRectF rect(QPoint(0,0), d->frame->frameSize);
0290         return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin);
0291     } else {
0292         return QRectF();
0293     }
0294 }
0295 
0296 QPixmap FrameSvg::alphaMask() const
0297 {
0298     //FIXME: the distinction between overlay and
0299     return d->alphaMask();
0300 }
0301 
0302 QRegion FrameSvg::mask() const
0303 {
0304     QRegion result;
0305     if (!d->frame) {
0306         return result;
0307     }
0308 
0309     QString id = d->cacheId(d->frame.data(), QString());
0310 
0311     QRegion* obj = d->frame->cachedMasks.object(id);
0312 
0313     if (!obj) {
0314         obj = new QRegion(QBitmap(d->alphaMask().mask()));
0315         result = *obj;
0316         d->frame->cachedMasks.insert(id, obj);
0317     }
0318     else {
0319         result = *obj;
0320     }
0321     return result;
0322 }
0323 
0324 void FrameSvg::setCacheAllRenderedFrames(bool cache)
0325 {
0326     if (d->cacheAll && !cache) {
0327         clearCache();
0328     }
0329 
0330     d->cacheAll = cache;
0331 }
0332 
0333 bool FrameSvg::cacheAllRenderedFrames() const
0334 {
0335     return d->cacheAll;
0336 }
0337 
0338 void FrameSvg::clearCache()
0339 {
0340     if (d->frame) {
0341         d->frame->cachedBackground = QPixmap();
0342         d->frame->cachedMasks.clear();
0343     }
0344     if (d->maskFrame) {
0345         d->maskFrame->cachedBackground = QPixmap();
0346         d->maskFrame->cachedMasks.clear();
0347     }
0348 }
0349 
0350 QPixmap FrameSvg::framePixmap()
0351 {
0352     if (d->frame->cachedBackground.isNull()) {
0353         d->generateBackground(d->frame);
0354     }
0355 
0356     return d->frame->cachedBackground;
0357 }
0358 
0359 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source)
0360 {
0361     if (d->frame->cachedBackground.isNull()) {
0362         d->generateBackground(d->frame);
0363         if (d->frame->cachedBackground.isNull()) {
0364             return;
0365         }
0366     }
0367 
0368     painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target);
0369 }
0370 
0371 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos)
0372 {
0373     if (d->frame->cachedBackground.isNull()) {
0374         d->generateBackground(d->frame);
0375         if (d->frame->cachedBackground.isNull()) {
0376             return;
0377         }
0378     }
0379 
0380     painter->drawPixmap(pos, d->frame->cachedBackground);
0381 }
0382 
0383 //#define DEBUG_FRAMESVG_CACHE
0384 FrameSvgPrivate::~FrameSvgPrivate() = default;
0385 
0386 QPixmap FrameSvgPrivate::alphaMask()
0387 {
0388     QString maskPrefix;
0389 
0390     if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) {
0391         maskPrefix = QStringLiteral("mask-");
0392     }
0393 
0394     if (maskPrefix.isNull()) {
0395         if (frame->cachedBackground.isNull()) {
0396             generateBackground(frame);
0397         }
0398         return frame->cachedBackground;
0399     }
0400 
0401     // We are setting the prefix only temporary to generate
0402     // the needed mask image
0403     const QString maskRequestedPrefix = requestedPrefix.isEmpty() ? QStringLiteral("mask") : maskPrefix % requestedPrefix;
0404     maskPrefix = maskPrefix % prefix;
0405 
0406     if (!maskFrame) {
0407         maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix);
0408         if (!maskFrame->cachedBackground.isNull()) {
0409             return maskFrame->cachedBackground;
0410         }
0411         updateSizes(maskFrame);
0412         generateBackground(maskFrame);
0413         return maskFrame->cachedBackground;
0414     }
0415 
0416     const bool shouldUpdate = maskFrame->enabledBorders != frame->enabledBorders
0417         || maskFrame->frameSize != frameSize(frame.data())
0418         || maskFrame->imagePath != frame->imagePath;
0419     if (shouldUpdate) {
0420         maskFrame = lookupOrCreateMaskFrame(frame, maskPrefix, maskRequestedPrefix);
0421         if (!maskFrame->cachedBackground.isNull()) {
0422             return maskFrame->cachedBackground;
0423         }
0424         updateSizes(maskFrame);
0425     }
0426 
0427     if (maskFrame->cachedBackground.isNull()) {
0428         generateBackground(maskFrame);
0429     }
0430 
0431     return maskFrame->cachedBackground;
0432 }
0433 
0434 QSharedPointer<FrameData> FrameSvgPrivate::lookupOrCreateMaskFrame(const QSharedPointer<FrameData> &frame, const QString &maskPrefix, const QString &maskRequestedPrefix)
0435 {
0436     const QString key = cacheId(frame.data(), maskPrefix);
0437     QSharedPointer<FrameData> mask = s_sharedFrames[q->theme()->d].value(key);
0438 
0439     // See if we can find a suitable candidate in the shared frames.
0440     // If there is one, use it.
0441     if (mask) {
0442         return mask;
0443     }
0444 
0445     mask.reset(new FrameData(*frame.data(), q));
0446     mask->prefix = maskPrefix;
0447     mask->requestedPrefix = maskRequestedPrefix;
0448     mask->theme = q->theme()->d;
0449     mask->imagePath = frame->imagePath;
0450     mask->enabledBorders = frame->enabledBorders;
0451     mask->frameSize = frameSize(frame).toSize();
0452     mask->cacheId = key;
0453     s_sharedFrames[q->theme()->d].insert(key, mask);
0454 
0455     return mask;
0456 }
0457 
0458 void FrameSvgPrivate::generateBackground(const QSharedPointer<FrameData> &frame)
0459 {
0460     if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->prefix)) {
0461         return;
0462     }
0463 
0464     const QString id = cacheId(frame.data(), frame->prefix);
0465 
0466     bool frameCached = !frame->cachedBackground.isNull();
0467     bool overlayCached = false;
0468     const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay"));
0469     QPixmap overlay;
0470     if (q->isUsingRenderingCache()) {
0471         frameCached = q->theme()->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull();
0472 
0473         if (overlayAvailable) {
0474             overlayCached = q->theme()->findInCache(QLatin1String("overlay_") % id, overlay) && !overlay.isNull();
0475         }
0476     }
0477 
0478     if (!frameCached) {
0479         generateFrameBackground(frame);
0480     }
0481 
0482     //Overlays
0483     QSize overlaySize;
0484     QPoint actualOverlayPos = QPoint(0, 0);
0485     if (overlayAvailable && !overlayCached) {
0486         overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay"));
0487 
0488         if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) {
0489             actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width());
0490         } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) {
0491             actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height());
0492             //Stretched or Tiled?
0493         } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) {
0494             overlaySize = frameSize(frame).toSize();
0495         } else {
0496             if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) {
0497                 overlaySize.setWidth(frameSize(frame).width());
0498             }
0499             if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) {
0500                 overlaySize.setHeight(frameSize(frame).height());
0501             }
0502         }
0503 
0504         overlay = alphaMask();
0505         QPainter overlayPainter(&overlay);
0506         overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
0507         //Tiling?
0508         if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) ||
0509                 q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) {
0510 
0511             QSize s = q->size();
0512             q->resize(q->elementSize(frame->prefix % QLatin1String("overlay")));
0513 
0514             overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay")));
0515             q->resize(s);
0516         } else {
0517             q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay"));
0518         }
0519 
0520         overlayPainter.end();
0521     }
0522 
0523     if (!frameCached) {
0524         cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap());
0525     }
0526 
0527     if (!overlay.isNull()) {
0528         QPainter p(&frame->cachedBackground);
0529         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
0530         p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize));
0531     }
0532 }
0533 
0534 void FrameSvgPrivate::generateFrameBackground(const QSharedPointer<FrameData> &frame)
0535 {
0536     //qCDebug(LOG_PLASMA) << "generating background";
0537     const QSize size = frameSize(frame).toSize() * q->devicePixelRatio();
0538 
0539     if (!size.isValid()) {
0540 #ifndef NDEBUG
0541         // qCDebug(LOG_PLASMA) << "Invalid frame size" << size;
0542 #endif
0543         return;
0544     }
0545     if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) {
0546         qCWarning(LOG_PLASMA) << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size;
0547         return;
0548     }
0549 
0550     frame->cachedBackground = QPixmap(size);
0551     frame->cachedBackground.fill(Qt::transparent);
0552     QPainter p(&frame->cachedBackground);
0553     p.setCompositionMode(QPainter::CompositionMode_Source);
0554     p.setRenderHint(QPainter::SmoothPixmapTransform);
0555 
0556     QRect contentRect = contentGeometry(frame, size);
0557     paintCenter(p, frame, contentRect, size);
0558 
0559     paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::TopBorder, contentRect);
0560     paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::TopBorder, contentRect);
0561     paintCorner(p, frame, FrameSvg::LeftBorder|FrameSvg::BottomBorder, contentRect);
0562     paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect);
0563 
0564     // Sides
0565     const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height();
0566     paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect);
0567     const int rightHeight = q->elementSize(frame->prefix % QLatin1String("right")).height();
0568     paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, rightHeight) * q->devicePixelRatio(), contentRect);
0569 
0570     const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width();
0571     paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect);
0572     const int bottomWidth = q->elementSize(frame->prefix % QLatin1String("bottom")).width();
0573     paintBorder(p, frame, FrameSvg::BottomBorder, QSize(bottomWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect);
0574     p.end();
0575 
0576     frame->cachedBackground.setDevicePixelRatio(q->devicePixelRatio());
0577 }
0578 
0579 QRect FrameSvgPrivate::contentGeometry(const QSharedPointer<FrameData> &frame, const QSize& size) const
0580 {
0581     const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(),
0582                             size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio());
0583     QRect contentRect(QPoint(0,0), contentSize);
0584     if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) {
0585         contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0);
0586     }
0587 
0588     // Corners
0589     if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) {
0590         contentRect.translate(0, frame->topHeight * q->devicePixelRatio());
0591     }
0592     return contentRect;
0593 }
0594 
0595 void FrameSvgPrivate::updateFrameData(UpdateType updateType)
0596 {
0597     auto fd = frame;
0598     QString newKey;
0599 
0600     if (fd) {
0601         const QString oldKey = fd->cacheId;
0602 
0603         const QString oldPath = fd->imagePath;
0604         const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders;
0605         const QSize currentSize = fd->frameSize;
0606 
0607         fd->enabledBorders = enabledBorders;
0608         fd->frameSize = pendingFrameSize;
0609         fd->imagePath = q->imagePath();
0610 
0611         newKey = cacheId(fd.data(), prefix);
0612 
0613         //reset frame to old values
0614         fd->enabledBorders = oldBorders;
0615         fd->frameSize = currentSize;
0616         fd->imagePath = oldPath;
0617 
0618         //FIXME: something more efficient than string comparison?
0619         if (oldKey == newKey) {
0620             return;
0621         }
0622 
0623         //qCDebug(LOG_PLASMA) << "looking for" << newKey;
0624         auto newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey);
0625         if (newFd) {
0626             //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount;
0627             // we've found a match, use that one
0628             Q_ASSERT(newKey == newFd.lock()->cacheId);
0629             frame = newFd;
0630             return;
0631         }
0632 
0633         fd.reset(new FrameData(*fd, q));
0634     } else {
0635         fd.reset(new FrameData(q, QString()));
0636     }
0637 
0638     frame = fd;
0639     fd->prefix = prefix;
0640     fd->requestedPrefix = requestedPrefix;
0641     //updateSizes();
0642     fd->enabledBorders = enabledBorders;
0643     fd->frameSize = pendingFrameSize;
0644     fd->imagePath = q->imagePath();
0645     //was fd just created empty now?
0646     if (newKey.isEmpty()) {
0647         newKey = cacheId(fd.data(), prefix);
0648     }
0649 
0650     // we know it isn't in s_sharedFrames due to the check above, so insert it now
0651     FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd);
0652     fd->cacheId = newKey;
0653     fd->theme = q->theme()->d;
0654     if (updateType == UpdateFrameAndMargins) {
0655         updateAndSignalSizes();
0656     } else {
0657         updateSizes(frame);
0658     }
0659 }
0660 
0661 void FrameSvgPrivate::paintCenter(QPainter& p, const QSharedPointer<FrameData> &frame, const QRect& contentRect, const QSize& fullSize)
0662 {
0663     if (!contentRect.isEmpty()) {
0664         const QString centerElementId = frame->prefix % QLatin1String("center");
0665         if (frame->tileCenter) {
0666             QSize centerTileSize = q->elementSize(centerElementId);
0667             QPixmap center(centerTileSize);
0668             center.fill(Qt::transparent);
0669 
0670             QPainter centerPainter(&center);
0671             centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
0672             q->paint(&centerPainter, QRect(QPoint(0, 0), centerTileSize),centerElementId);
0673 
0674             if (frame->composeOverBorder) {
0675                 p.drawTiledPixmap(QRect(QPoint(0, 0), fullSize), center);
0676             } else {
0677                 p.drawTiledPixmap(FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), center);
0678             }
0679         } else {
0680             if (frame->composeOverBorder) {
0681                 q->paint(&p, QRect(QPoint(0, 0), fullSize),
0682                          centerElementId);
0683             } else {
0684                 q->paint(&p, FrameSvgHelpers::sectionRect(FrameSvg::NoBorder, contentRect, fullSize * q->devicePixelRatio()), centerElementId);
0685             }
0686         }
0687     }
0688 
0689     if (frame->composeOverBorder) {
0690         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
0691         p.drawPixmap(QRect(QPoint(0, 0), fullSize), alphaMask());
0692         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
0693     }
0694 }
0695 
0696 void FrameSvgPrivate::paintBorder(QPainter& p, const QSharedPointer<FrameData> &frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const
0697 {
0698     QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders);
0699     if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) {
0700         if (frame->stretchBorders) {
0701             q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side);
0702         } else {
0703             QPixmap px(size);
0704             px.fill(Qt::transparent);
0705 
0706             QPainter sidePainter(&px);
0707             sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
0708             q->paint(&sidePainter, QRect(QPoint(0, 0), size), side);
0709 
0710             p.drawTiledPixmap(FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), px);
0711         }
0712     }
0713 }
0714 
0715 void FrameSvgPrivate::paintCorner(QPainter& p, const QSharedPointer<FrameData> &frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const
0716 {
0717     // Draw the corner only if both borders in both directions are enabled.
0718     if ((frame->enabledBorders & border) != border) {
0719         return;
0720     }
0721     const QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border);
0722     if (q->hasElement(corner)) {
0723         q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner);
0724     }
0725 }
0726 
0727 QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const
0728 {
0729     const QSize size = frameSize(frame).toSize();
0730     const QLatin1Char s('_');
0731     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;
0732 }
0733 
0734 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay)
0735 {
0736     if (!q->isUsingRenderingCache()) {
0737         return;
0738     }
0739 
0740     //insert background
0741     if (!frame) {
0742         return;
0743     }
0744 
0745     const QString id = cacheId(frame.data(), prefixToSave);
0746 
0747     //qCDebug(LOG_PLASMA)<<"Saving to cache frame"<<id;
0748 
0749     q->theme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave);
0750 
0751     if (!overlay.isNull()) {
0752         //insert overlay
0753         q->theme()->insertIntoCache(QLatin1String("overlay_") % id, overlay, QString::number((qint64)q, 16) % prefixToSave % QLatin1String("overlay"));
0754     }
0755 }
0756 
0757 void FrameSvgPrivate::updateSizes(FrameData *frame) const
0758 {
0759     //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix;
0760     Q_ASSERT(frame);
0761 
0762     QSize s = q->size();
0763     q->resize();
0764     if (!frame->cachedBackground.isNull()) {
0765         frame->cachedBackground = QPixmap();
0766     }
0767 
0768     //This has the same size regardless the border is enabled or not
0769     frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height();
0770 
0771     if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) {
0772         frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height();
0773     } else {
0774         frame->fixedTopMargin = frame->fixedTopHeight;
0775     }
0776 
0777     //The same, but its size depends from the margin being enabled
0778     if (frame->enabledBorders & FrameSvg::TopBorder) {
0779         frame->topMargin = frame->fixedTopMargin;
0780         frame->topHeight = frame->fixedTopHeight;
0781     } else {
0782         frame->topMargin = frame->topHeight = 0;
0783     }
0784 
0785     frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width();
0786 
0787     if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) {
0788         frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width();
0789     } else {
0790         frame->fixedLeftMargin = frame->fixedLeftWidth;
0791     }
0792 
0793     if (frame->enabledBorders & FrameSvg::LeftBorder) {
0794         frame->leftMargin = frame->fixedLeftMargin;
0795         frame->leftWidth = frame->fixedLeftWidth;
0796     } else {
0797         frame->leftMargin = frame->leftWidth = 0;
0798     }
0799 
0800     frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width();
0801 
0802     if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) {
0803         frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width();
0804     } else {
0805         frame->fixedRightMargin = frame->fixedRightWidth;
0806     }
0807 
0808     if (frame->enabledBorders & FrameSvg::RightBorder) {
0809         frame->rightMargin = frame->fixedRightMargin;
0810         frame->rightWidth = frame->fixedRightWidth;
0811     } else {
0812         frame->rightMargin = frame->rightWidth = 0;
0813     }
0814 
0815     frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height();
0816 
0817     if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) {
0818         frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height();
0819     } else {
0820         frame->fixedBottomMargin = frame->fixedBottomHeight;
0821     }
0822 
0823     if (frame->enabledBorders & FrameSvg::BottomBorder) {
0824         frame->bottomMargin = frame->fixedBottomMargin;
0825         frame->bottomHeight = frame->fixedBottomHeight;
0826     } else {
0827         frame->bottomMargin = frame->bottomHeight = 0;
0828     }
0829 
0830     frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) &&
0831                                 q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center")));
0832 
0833     //since it's rectangular, topWidth and bottomWidth must be the same
0834     //the ones that don't have a frame->prefix is for retrocompatibility
0835     frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center")));
0836     frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding")));
0837     frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders")));
0838     q->resize(s);
0839 }
0840 
0841 void FrameSvgPrivate::updateNeeded()
0842 {
0843     q->setElementPrefix(requestedPrefix);
0844     //frame not created yet?
0845     if (!frame) {
0846         return;
0847     }
0848     q->clearCache();
0849     updateSizes(frame);
0850 }
0851 
0852 void FrameSvgPrivate::updateAndSignalSizes()
0853 {
0854     //frame not created yet?
0855     if (!frame) {
0856         return;
0857     }
0858     updateSizes(frame);
0859     emit q->repaintNeeded();
0860 }
0861 
0862 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const
0863 {
0864     if (!frame) {
0865         return QSizeF();
0866     }
0867 
0868     if (!frame->frameSize.isValid()) {
0869         updateSizes(frame);
0870         frame->frameSize = q->size();
0871     }
0872 
0873     return frame->frameSize;
0874 }
0875 
0876 QString FrameSvg::actualPrefix() const
0877 {
0878     return d->prefix;
0879 }
0880 
0881 bool FrameSvg::isRepaintBlocked() const
0882 {
0883     return d->repaintBlocked;
0884 }
0885 
0886 void FrameSvg::setRepaintBlocked(bool blocked)
0887 {
0888     d->repaintBlocked = blocked;
0889 
0890     if (!blocked) {
0891         d->updateFrameData();
0892     }
0893 }
0894 
0895 } // Plasma namespace