22 #include "private/framesvg_p.h"
26 #include <QCryptographicHash>
30 #include <QStringBuilder>
37 #include <private/svg_p.h>
51 d(new FrameSvgPrivate(this))
54 d->frames.insert(
QString(),
new FrameData(
this));
68 bool updateNeeded =
true;
71 FrameData *fd = d->frames[d->prefix];
72 if (fd->refcount() == 1) {
75 const QString oldKey = d->cacheId(fd, d->prefix);
76 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
83 Svg::d->setImagePath(path);
87 FrameData *oldFd = d->frames[d->prefix];
88 const QString key = d->cacheId(oldFd, d->prefix);
89 fd = FrameSvgPrivate::s_sharedFrames.value(key);
100 fd =
new FrameData(*oldFd,
this);
103 d->frames.insert(d->prefix, fd);
109 const QString key = d->cacheId(fd, d->prefix);
110 FrameSvgPrivate::s_sharedFrames.insert(key, fd);
113 d->updateAndSignalSizes();
121 if (borders == d->frames[d->prefix]->enabledBorders) {
125 FrameData *fd = d->frames[d->prefix];
127 const QString oldKey = d->cacheId(fd, d->prefix);
128 const EnabledBorders oldBorders = fd->enabledBorders;
129 fd->enabledBorders = borders;
130 const QString newKey = d->cacheId(fd, d->prefix);
131 fd->enabledBorders = oldBorders;
134 FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
139 d->frames.insert(d->prefix, newFd);
142 if (fd->deref(
this)) {
145 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
152 if (fd->refcount() == 1) {
155 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
161 fd =
new FrameData(*fd,
this);
162 d->frames.insert(d->prefix, fd);
165 fd->enabledBorders = borders;
166 d->updateAndSignalSizes();
171 if (d->frames.isEmpty()) {
177 if (it != d->frames.constEnd()) {
178 return it.value()->enabledBorders;
204 d->location = location;
209 const QString oldPrefix(d->prefix);
215 if (!d->prefix.isEmpty()) {
220 FrameData *oldFrameData = d->frames.value(oldPrefix);
221 if (oldPrefix == d->prefix && oldFrameData) {
225 if (!d->frames.contains(d->prefix)) {
227 FrameData *newFd = 0;
228 if (!oldFrameData->frameSize.isEmpty()) {
229 const QString key = d->cacheId(oldFrameData, d->prefix);
230 newFd = FrameSvgPrivate::s_sharedFrames.value(key);
235 const bool cache = !newFd && !oldFrameData->frameSize.isEmpty();
239 newFd =
new FrameData(*oldFrameData,
this);
242 d->frames.insert(d->prefix, newFd);
247 const QString key = d->cacheId(oldFrameData, d->prefix);
250 FrameSvgPrivate::s_sharedFrames.insert(key, newFd);
255 FrameData *newFd =
new FrameData(
this);
256 d->frames.insert(d->prefix, newFd);
263 d->frames.remove(oldPrefix);
265 if (oldFrameData->deref(
this)) {
266 const QString oldKey = d->cacheId(oldFrameData, oldPrefix);
267 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
310 if (d->prefix.isEmpty()) {
314 return d->prefix.left(d->prefix.size() - 1);
328 FrameData *fd = d->frames[d->prefix];
329 if (size == fd->frameSize) {
333 const QString oldKey = d->cacheId(fd, d->prefix);
334 const QSize currentSize = fd->frameSize;
335 fd->frameSize = size.
toSize();
336 const QString newKey = d->cacheId(fd, d->prefix);
337 fd->frameSize = currentSize;
340 FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey);
345 d->frames.insert(d->prefix, newFd);
348 if (fd->deref(
this)) {
351 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
358 if (fd->refcount() == 1) {
361 FrameSvgPrivate::s_sharedFrames.remove(oldKey);
367 fd =
new FrameData(*fd,
this);
368 d->frames.insert(d->prefix, fd);
372 fd->frameSize = size.
toSize();
374 FrameSvgPrivate::s_sharedFrames.insert(newKey, fd);
381 if (it == d->frames.constEnd()) {
382 return QSize(-1, -1);
384 return d->frameSize(it.value());
390 if (d->frames[d->prefix]->noBorderPadding) {
396 return d->frames[d->prefix]->topMargin;
400 return d->frames[d->prefix]->leftMargin;
404 return d->frames[d->prefix]->rightMargin;
409 return d->frames[d->prefix]->bottomMargin;
416 FrameData *frame = d->frames[d->prefix];
418 if (frame->noBorderPadding) {
419 left = top = right = bottom = 0;
423 top = frame->topMargin;
424 left = frame->leftMargin;
425 right = frame->rightMargin;
426 bottom = frame->bottomMargin;
435 FrameData *frame = d->frames[d->prefix];
437 return rect.
adjusted(frame->leftMargin, frame->topMargin,
438 -frame->rightMargin, -frame->bottomMargin);
447 return d->alphaMask();
452 FrameData *frame = d->frames[d->prefix];
454 if (!frame->cachedMasks.contains(
id)) {
456 if (frame->cachedMasks.count() > frame->MAX_CACHED_MASKS) {
457 frame->cachedMasks.clear();
459 frame->cachedMasks.insert(
id,
QRegion(
QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black))));
461 return frame->cachedMasks[id];
466 if (d->cacheAll && !cache) {
480 FrameData *frame = d->frames[d->prefix];
485 FrameData *p = it.
next().value();
488 if (p->deref(
this)) {
490 FrameSvgPrivate::s_sharedFrames.remove(key);
491 p->cachedBackground =
QPixmap();
501 FrameData *frame = d->frames[d->prefix];
502 if (frame->cachedBackground.isNull()) {
503 d->generateBackground(frame);
504 if (frame->cachedBackground.isNull()) {
509 return frame->cachedBackground;
514 FrameData *frame = d->frames[d->prefix];
515 if (frame->cachedBackground.isNull()) {
516 d->generateBackground(frame);
517 if (frame->cachedBackground.isNull()) {
522 painter->
drawPixmap(target, frame->cachedBackground, source.
isValid() ? source : target);
527 FrameData *frame = d->frames[d->prefix];
528 if (frame->cachedBackground.isNull()) {
529 d->generateBackground(frame);
530 if (frame->cachedBackground.isNull()) {
535 painter->
drawPixmap(pos, frame->cachedBackground);
539 FrameSvgPrivate::~FrameSvgPrivate()
541 #ifdef DEBUG_FRAMESVG_CACHE
542 kDebug() <<
"*************" << q << q->imagePath() <<
"****************";
546 while (it.hasNext()) {
551 if (it.value()->removeRefs(q)) {
552 const QString key = cacheId(it.value(), it.key());
553 #ifdef DEBUG_FRAMESVG_CACHE
554 kDebug() <<
"2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames.contains(key);
556 s_sharedFrames.remove(key);
559 #ifdef DEBUG_FRAMESVG_CACHE
561 kDebug() <<
"still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed();
564 kDebug() <<
"lost our value for" << it.key();
569 #ifdef DEBUG_FRAMESVG_CACHE
572 while (it2.hasNext()) {
574 const int rc = it2.value()->refcount();
576 kDebug() <<
" LOST!" << it2.key() << rc << it2.value();
578 kDebug() <<
" " << it2.key() << rc << it2.value();
579 foreach (FrameSvg *data, it2.value()->references.keys()) {
580 kDebug( )<<
" " << (
void*)data << it2.value()->references[data];
585 kDebug() <<
"#####################################" << s_sharedFrames.count() <<
", pixmaps saved:" << shares;
591 QPixmap FrameSvgPrivate::alphaMask()
593 FrameData *frame = frames[prefix];
596 if (q->hasElement(
"mask-" % prefix %
"center")) {
597 maskPrefix =
"mask-";
600 if (maskPrefix.
isNull()) {
601 if (frame->cachedBackground.isNull()) {
602 generateBackground(frame);
603 if (frame->cachedBackground.isNull()) {
608 return frame->cachedBackground;
614 prefix = maskPrefix % oldPrefix;
616 if (!frames.contains(prefix)) {
617 const QString key = cacheId(frame, prefix);
621 FrameData *maskFrame = s_sharedFrames.value(key);
626 maskFrame =
new FrameData(*frame, q);
627 s_sharedFrames.insert(key, maskFrame);
630 frames.insert(prefix, maskFrame);
634 FrameData *maskFrame = frames[prefix];
635 if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) {
636 const QString oldKey = cacheId(maskFrame, prefix);
637 maskFrame->frameSize = frameSize(frame).toSize();
638 const QString newKey = cacheId(maskFrame, prefix);
639 if (s_sharedFrames.contains(oldKey)) {
640 s_sharedFrames.remove(oldKey);
641 s_sharedFrames.insert(newKey, maskFrame);
644 maskFrame->cachedBackground =
QPixmap();
646 generateBackground(maskFrame);
647 if (maskFrame->cachedBackground.isNull()) {
653 return maskFrame->cachedBackground;
657 void FrameSvgPrivate::generateBackground(FrameData *frame)
659 if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) {
663 const QString id = cacheId(frame, prefix);
666 bool frameCached = !frame->cachedBackground.isNull();
667 bool overlayCached =
false;
668 const bool overlayAvailable = !prefix.startsWith(
QLatin1String(
"mask-")) && q->hasElement(prefix %
"overlay");
670 if (q->isUsingRenderingCache()) {
671 frameCached = theme->findInCache(
id, frame->cachedBackground) && !frame->cachedBackground.
isNull();
673 if (overlayAvailable) {
674 overlayCached = theme->findInCache(
"overlay_" %
id, overlay) && !overlay.
isNull();
679 generateFrameBackground(frame);
685 if (overlayAvailable && !overlayCached) {
687 overlaySize = q->elementSize(prefix %
"overlay");
690 if (q->hasElement(prefix %
"hint-overlay-random-pos")) {
691 actualOverlayPos = overlayPos;
692 }
else if (q->hasElement(prefix %
"hint-overlay-pos-right")) {
693 actualOverlayPos.
setX(frame->frameSize.width() - overlaySize.
width());
694 }
else if (q->hasElement(prefix %
"hint-overlay-pos-bottom")) {
695 actualOverlayPos.
setY(frame->frameSize.height() - overlaySize.
height());
697 }
else if (q->hasElement(prefix %
"hint-overlay-stretch")) {
698 overlaySize = frameSize(frame).toSize();
700 if (q->hasElement(prefix %
"hint-overlay-tile-horizontal")) {
701 overlaySize.
setWidth(frameSize(frame).width());
703 if (q->hasElement(prefix %
"hint-overlay-tile-vertical")) {
704 overlaySize.
setHeight(frameSize(frame).height());
708 overlay = alphaMask();
710 overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
712 if (q->hasElement(prefix %
"hint-overlay-tile-horizontal") ||
713 q->hasElement(prefix %
"hint-overlay-tile-vertical")) {
716 q->resize(q->elementSize(prefix %
"overlay"));
718 overlayPainter.drawTiledPixmap(
QRect(
QPoint(0,0), overlaySize), q->pixmap(prefix %
"overlay"));
721 q->paint(&overlayPainter,
QRect(actualOverlayPos, overlaySize), prefix %
"overlay");
724 overlayPainter.end();
728 cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay :
QPixmap());
732 QPainter p(&frame->cachedBackground);
733 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
734 p.drawPixmap(actualOverlayPos, overlay,
QRect(actualOverlayPos, overlaySize));
738 void FrameSvgPrivate::generateFrameBackground(FrameData *frame)
741 const QSizeF size = frameSize(frame);
742 const int topWidth = q->elementSize(prefix %
"top").width();
743 const int leftHeight = q->elementSize(prefix %
"left").height();
744 const int topOffset = 0;
745 const int leftOffset = 0;
749 kDebug() <<
"Invalid frame size" << size;
753 kWarning() <<
"Not generating frame background for a size whose width or height is more than" <<
MAX_FRAME_SIZE << size;
757 const int contentWidth = size.
width() - frame->leftWidth - frame->rightWidth;
758 const int contentHeight = size.
height() - frame->topHeight - frame->bottomHeight;
761 int rightOffset = contentWidth;
762 int bottomOffset = contentHeight;
764 frame->cachedBackground =
QPixmap(frame->leftWidth + contentWidth + frame->rightWidth,
765 frame->topHeight + contentHeight + frame->bottomHeight);
766 frame->cachedBackground.fill(Qt::transparent);
767 QPainter p(&frame->cachedBackground);
768 p.setCompositionMode(QPainter::CompositionMode_Source);
769 p.setRenderHint(QPainter::SmoothPixmapTransform);
772 if (frame->tileCenter) {
773 if (contentHeight > 0 && contentWidth > 0) {
774 const int centerTileHeight = q->elementSize(prefix %
"center").height();
775 const int centerTileWidth = q->elementSize(prefix %
"center").width();
776 QPixmap center(centerTileWidth, centerTileHeight);
777 center.fill(Qt::transparent);
781 centerPainter.setCompositionMode(QPainter::CompositionMode_Source);
782 q->paint(¢erPainter,
QRect(
QPoint(0, 0), q->elementSize(prefix %
"center")), prefix %
"center");
785 if (frame->composeOverBorder) {
788 p.drawTiledPixmap(
QRect(frame->leftWidth, frame->topHeight,
789 contentWidth, contentHeight), center);
793 if (contentHeight > 0 && contentWidth > 0) {
794 if (frame->composeOverBorder) {
798 q->paint(&p,
QRect(frame->leftWidth, frame->topHeight,
799 contentWidth, contentHeight),
805 if (frame->composeOverBorder) {
806 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
808 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
812 rightOffset += frame->leftWidth;
817 contentTop = frame->topHeight;
818 bottomOffset += frame->topHeight;
821 q->paint(&p,
QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix %
"topleft");
823 contentLeft = frame->leftWidth;
827 q->paint(&p,
QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix %
"topright");
833 q->paint(&p,
QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix %
"bottomleft");
835 contentLeft = frame->leftWidth;
839 q->paint(&p,
QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix %
"bottomright");
844 if (frame->stretchBorders) {
846 if (q->hasElement(prefix %
"left") &&
849 q->paint(&p,
QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix %
"left");
852 if (q->hasElement(prefix %
"right") &&
855 q->paint(&p,
QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix %
"right");
862 q->paint(&p,
QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix %
"top");
867 q->paint(&p,
QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix %
"bottom");
872 && leftHeight > 0 && frame->leftWidth > 0) {
873 QPixmap left(frame->leftWidth, leftHeight);
874 left.fill(Qt::transparent);
877 sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
878 q->paint(&sidePainter,
QRect(
QPoint(0, 0), left.size()), prefix %
"left");
880 p.drawTiledPixmap(
QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left);
884 leftHeight > 0 && frame->rightWidth > 0) {
885 QPixmap right(frame->rightWidth, leftHeight);
886 right.fill(Qt::transparent);
889 sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
890 q->paint(&sidePainter,
QRect(
QPoint(0, 0), right.size()), prefix %
"right");
892 p.drawTiledPixmap(
QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right);
896 && topWidth > 0 && frame->topHeight > 0) {
897 QPixmap top(topWidth, frame->topHeight);
898 top.fill(Qt::transparent);
901 sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
902 q->paint(&sidePainter,
QRect(
QPoint(0, 0), top.size()), prefix %
"top");
904 p.drawTiledPixmap(
QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top);
908 && topWidth > 0 && frame->bottomHeight > 0) {
909 QPixmap bottom(topWidth, frame->bottomHeight);
910 bottom.fill(Qt::transparent);
913 sidePainter.setCompositionMode(QPainter::CompositionMode_Source);
914 q->paint(&sidePainter,
QRect(
QPoint(0, 0), bottom.size()), prefix %
"bottom");
916 p.drawTiledPixmap(
QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom);
922 QString FrameSvgPrivate::cacheId(FrameData *frame,
const QString &prefixToSave)
const
929 void FrameSvgPrivate::cacheFrame(
const QString &prefixToSave,
const QPixmap &background,
const QPixmap &overlay)
931 if (!q->isUsingRenderingCache()) {
936 FrameData *frame = frames.value(prefixToSave);
942 const QString id = cacheId(frame, prefixToSave);
954 void FrameSvgPrivate::updateSizes()
const
957 FrameData *frame = frames[prefix];
962 frame->cachedBackground =
QPixmap();
965 frame->topHeight = q->elementSize(prefix %
"top").height();
967 if (q->hasElement(prefix %
"hint-top-margin")) {
968 frame->topMargin = q->elementSize(prefix %
"hint-top-margin").height();
970 frame->topMargin = frame->topHeight;
973 frame->topMargin = frame->topHeight = 0;
977 frame->leftWidth = q->elementSize(prefix %
"left").width();
979 if (q->hasElement(prefix %
"hint-left-margin")) {
980 frame->leftMargin = q->elementSize(prefix %
"hint-left-margin").width();
982 frame->leftMargin = frame->leftWidth;
985 frame->leftMargin = frame->leftWidth = 0;
989 frame->rightWidth = q->elementSize(prefix %
"right").width();
991 if (q->hasElement(prefix %
"hint-right-margin")) {
992 frame->rightMargin = q->elementSize(prefix %
"hint-right-margin").width();
994 frame->rightMargin = frame->rightWidth;
997 frame->rightMargin = frame->rightWidth = 0;
1001 frame->bottomHeight = q->elementSize(prefix %
"bottom").height();
1003 if (q->hasElement(prefix %
"hint-bottom-margin")) {
1004 frame->bottomMargin = q->elementSize(prefix %
"hint-bottom-margin").height();
1006 frame->bottomMargin = frame->bottomHeight;
1009 frame->bottomMargin = frame->bottomHeight = 0;
1012 frame->composeOverBorder = (q->hasElement(prefix %
"hint-compose-over-border") &&
1013 q->hasElement(
"mask-" % prefix %
"center"));
1017 frame->tileCenter = (q->hasElement(
"hint-tile-center") || q->hasElement(prefix %
"hint-tile-center"));
1018 frame->noBorderPadding = (q->hasElement(
"hint-no-border-padding") || q->hasElement(prefix %
"hint-no-border-padding"));
1019 frame->stretchBorders = (q->hasElement(
"hint-stretch-borders") || q->hasElement(prefix %
"hint-stretch-borders"));
1023 void FrameSvgPrivate::updateNeeded()
1029 void FrameSvgPrivate::updateAndSignalSizes()
1032 emit q->repaintNeeded();
1035 QSizeF FrameSvgPrivate::frameSize(FrameData *frame)
const
1037 if (!frame->frameSize.isValid()) {
1039 frame->frameSize = q->size();
1042 return frame->frameSize;
1045 void FrameData::ref(FrameSvg *svg)
1047 references[svg] = references[svg] + 1;
1051 bool FrameData::deref(FrameSvg *svg)
1053 references[svg] = references[svg] - 1;
1055 if (references[svg] < 1) {
1056 references.remove(svg);
1059 return references.isEmpty();
1062 bool FrameData::removeRefs(FrameSvg *svg)
1064 references.remove(svg);
1065 return references.isEmpty();
1068 bool FrameData::isUsed()
const
1070 return !references.isEmpty();
1073 int FrameData::refcount()
const
1075 return references.count();
1080 #include "framesvg.moc"
Q_INVOKABLE void paintFrame(QPainter *painter, const QRectF &target, const QRectF &source=QRectF())
Paints the loaded SVG with the elements that represents the border.
Q_INVOKABLE QRegion mask() const
Returns a mask that tightly contains the fully opaque areas of the svg.
void setHeight(int height)
Q_INVOKABLE void setCacheAllRenderedFrames(bool cache)
Sets whether saving all the rendered prefixes in a cache or not.
void repaintNeeded()
Emitted whenever the SVG data has changed in such a way that a repaint is required.
Q_INVOKABLE QRectF contentsRect() const
QSize size() const
The size of the SVG.
QString imagePath() const
The SVG file to render.
FrameSvg(QObject *parent=0)
Constructs a new FrameSvg that paints the proper named subelements as borders.
Q_INVOKABLE bool hasElement(const QString &elementId) const
Check whether an element exists in the loaded SVG.
QString number(int n, int base)
Q_INVOKABLE void resizeFrame(const QSizeF &size)
Resize the frame maintaining the same border size.
QPixmap alphaMask() const
Location
The Location enumeration describes where on screen an element, such as an Applet or its managing cont...
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const
Q_INVOKABLE QString prefix()
Returns the prefix for SVG elements of the FrameSvg.
static const int MAX_FRAME_SIZE
Q_INVOKABLE void setImagePath(const QString &path)
Loads a new Svg.
Along the top of the screen.
void setContainsMultipleImages(bool multiple)
Set whether the SVG contains a single image or multiple ones.
Q_INVOKABLE void clearCache()
Deletes the internal cache freeing memory: use this if you want to switch the rendered element and yo...
Q_INVOKABLE bool cacheAllRenderedFrames() const
Along the bottom of the screen.
Q_INVOKABLE QPixmap framePixmap()
Returns a pixmap of the SVG represented by this object.
static Theme * defaultTheme()
Singleton pattern accessor.
EnabledBorders enabledBorders() const
Convenience method to get the enabled borders.
QRectF adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2) const
Q_INVOKABLE QSizeF frameSize() const
void insertIntoCache(const QString &key, const QPixmap &pix)
Insert specified pixmap into the cache.
Q_INVOKABLE void setElementPrefix(Plasma::Location location)
Sets the prefix (.
Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
Convenience method that extracts the size of the four margins in the four output parameters.
Along the right side of the screen.
Q_INVOKABLE qreal marginSize(const Plasma::MarginEdge edge) const
Returns the margin size given the margin edge we want.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Along the left side of the screen.
A theme aware image-centric SVG class.
void setEnabledBorders(const EnabledBorders borders)
Sets what borders should be painted.