Plasma
framesvg.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright 2008-2010 by Aaron Seigo <aseigo@kde.org> 00003 * Copyright 2008-2010 Marco Martin <notmart@gmail.com> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Library General Public License as 00007 * published by the Free Software Foundation; either version 2, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details 00014 * 00015 * You should have received a copy of the GNU Library General Public 00016 * License along with this program; if not, write to the 00017 * Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "framesvg.h" 00022 #include "private/framesvg_p.h" 00023 00024 #include <QAtomicInt> 00025 #include <QBitmap> 00026 #include <QCryptographicHash> 00027 #include <QPainter> 00028 #include <QRegion> 00029 #include <QSize> 00030 #include <QStringBuilder> 00031 #include <QTimer> 00032 00033 #include <kdebug.h> 00034 00035 #include <applet.h> 00036 #include <theme.h> 00037 #include <private/svg_p.h> 00038 00039 namespace Plasma 00040 { 00041 00042 00043 QHash<QString, FrameData *> FrameSvgPrivate::s_sharedFrames; 00044 00045 // Any attempt to generate a frame whose width or height is larger than this 00046 // will be rejected 00047 static const int MAX_FRAME_SIZE = 100000; 00048 00049 FrameSvg::FrameSvg(QObject *parent) 00050 : Svg(parent), 00051 d(new FrameSvgPrivate(this)) 00052 { 00053 connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded())); 00054 d->frames.insert(QString(), new FrameData(this)); 00055 } 00056 00057 FrameSvg::~FrameSvg() 00058 { 00059 delete d; 00060 } 00061 00062 void FrameSvg::setImagePath(const QString &path) 00063 { 00064 if (path == imagePath()) { 00065 return; 00066 } 00067 00068 bool updateNeeded = true; 00069 clearCache(); 00070 00071 FrameData *fd = d->frames[d->prefix]; 00072 if (fd->refcount() == 1) { 00073 // we're the only user of it, let's remove it from the shared keys 00074 // we don't want to deref it, however, as we'll still be using it 00075 const QString oldKey = d->cacheId(fd, d->prefix); 00076 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00077 } else { 00078 // others are using this frame, so deref it for ourselves 00079 fd->deref(this); 00080 fd = 0; 00081 } 00082 00083 Svg::d->setImagePath(path); 00084 00085 if (!fd) { 00086 // we need to replace our frame, start by looking in the frame cache 00087 FrameData *oldFd = d->frames[d->prefix]; 00088 const QString key = d->cacheId(oldFd, d->prefix); 00089 fd = FrameSvgPrivate::s_sharedFrames.value(key); 00090 00091 if (fd) { 00092 // we found one, so ref it and use it; we also don't need to (or want to!) 00093 // trigger a full update of the frame since it is already the one we want 00094 // and likely already rendered just fine 00095 fd->ref(this); 00096 updateNeeded = false; 00097 } else { 00098 // nothing exists for us in the cache, so create a new FrameData based 00099 // on the old one 00100 fd = new FrameData(*oldFd, this); 00101 } 00102 00103 d->frames.insert(d->prefix, fd); 00104 } 00105 00106 setContainsMultipleImages(true); 00107 if (updateNeeded) { 00108 // ensure our frame is in the cache 00109 const QString key = d->cacheId(fd, d->prefix); 00110 FrameSvgPrivate::s_sharedFrames.insert(key, fd); 00111 00112 // this will emit repaintNeeded() as well when it is done 00113 d->updateAndSignalSizes(); 00114 } else { 00115 emit repaintNeeded(); 00116 } 00117 } 00118 00119 void FrameSvg::setEnabledBorders(const EnabledBorders borders) 00120 { 00121 if (borders == d->frames[d->prefix]->enabledBorders) { 00122 return; 00123 } 00124 00125 FrameData *fd = d->frames[d->prefix]; 00126 00127 const QString oldKey = d->cacheId(fd, d->prefix); 00128 const EnabledBorders oldBorders = fd->enabledBorders; 00129 fd->enabledBorders = borders; 00130 const QString newKey = d->cacheId(fd, d->prefix); 00131 fd->enabledBorders = oldBorders; 00132 00133 //kDebug() << "looking for" << newKey; 00134 FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey); 00135 if (newFd) { 00136 //kDebug() << "FOUND IT!" << newFd->refcount; 00137 // we've found a math, so insert that new one and ref it .. 00138 newFd->ref(this); 00139 d->frames.insert(d->prefix, newFd); 00140 00141 //.. then deref the old one and if it's no longer used, get rid of it 00142 if (fd->deref(this)) { 00143 //const QString oldKey = d->cacheId(fd, d->prefix); 00144 //kDebug() << "1. Removing it" << oldKey << fd->refcount; 00145 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00146 delete fd; 00147 } 00148 00149 return; 00150 } 00151 00152 if (fd->refcount() == 1) { 00153 // we're the only user of it, let's remove it from the shared keys 00154 // we don't want to deref it, however, as we'll still be using it 00155 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00156 } else { 00157 // others are using it, but we wish to change its size. so deref it, 00158 // then create a copy of it (we're automatically ref'd via the ctor), 00159 // then insert it into our frames. 00160 fd->deref(this); 00161 fd = new FrameData(*fd, this); 00162 d->frames.insert(d->prefix, fd); 00163 } 00164 00165 fd->enabledBorders = borders; 00166 d->updateAndSignalSizes(); 00167 } 00168 00169 FrameSvg::EnabledBorders FrameSvg::enabledBorders() const 00170 { 00171 if (d->frames.isEmpty()) { 00172 return NoBorder; 00173 } 00174 00175 QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix); 00176 00177 if (it != d->frames.constEnd()) { 00178 return it.value()->enabledBorders; 00179 } else { 00180 return NoBorder; 00181 } 00182 } 00183 00184 void FrameSvg::setElementPrefix(Plasma::Location location) 00185 { 00186 switch (location) { 00187 case TopEdge: 00188 setElementPrefix("north"); 00189 break; 00190 case BottomEdge: 00191 setElementPrefix("south"); 00192 break; 00193 case LeftEdge: 00194 setElementPrefix("west"); 00195 break; 00196 case RightEdge: 00197 setElementPrefix("east"); 00198 break; 00199 default: 00200 setElementPrefix(QString()); 00201 break; 00202 } 00203 00204 d->location = location; 00205 } 00206 00207 void FrameSvg::setElementPrefix(const QString &prefix) 00208 { 00209 const QString oldPrefix(d->prefix); 00210 00211 if (!hasElement(prefix % "-center")) { 00212 d->prefix.clear(); 00213 } else { 00214 d->prefix = prefix; 00215 if (!d->prefix.isEmpty()) { 00216 d->prefix += '-'; 00217 } 00218 } 00219 00220 FrameData *oldFrameData = d->frames.value(oldPrefix); 00221 if (oldPrefix == d->prefix && oldFrameData) { 00222 return; 00223 } 00224 00225 if (!d->frames.contains(d->prefix)) { 00226 if (oldFrameData) { 00227 FrameData *newFd = 0; 00228 if (!oldFrameData->frameSize.isEmpty()) { 00229 const QString key = d->cacheId(oldFrameData, d->prefix); 00230 newFd = FrameSvgPrivate::s_sharedFrames.value(key); 00231 } 00232 00233 // we need to put this in the cache if we didn't find it in the shared frames 00234 // and we have a size; if we don't have a size, we'll catch it later 00235 const bool cache = !newFd && !oldFrameData->frameSize.isEmpty(); 00236 if (newFd) { 00237 newFd->ref(this); 00238 } else { 00239 newFd = new FrameData(*oldFrameData, this); 00240 } 00241 00242 d->frames.insert(d->prefix, newFd); 00243 00244 if (cache) { 00245 // we have to cache after inserting the frame since the cacheId requires the 00246 // frame to be in the frames collection already 00247 const QString key = d->cacheId(oldFrameData, d->prefix); 00248 //kDebug() << this << " 1. inserting as" << key; 00249 00250 FrameSvgPrivate::s_sharedFrames.insert(key, newFd); 00251 } 00252 } else { 00253 // couldn't find anything useful, so we just create something here 00254 // we don't have a size for it yet, so don't bother trying to share it just yet 00255 FrameData *newFd = new FrameData(this); 00256 d->frames.insert(d->prefix, newFd); 00257 } 00258 00259 d->updateSizes(); 00260 } 00261 00262 if (!d->cacheAll) { 00263 d->frames.remove(oldPrefix); 00264 if (oldFrameData) { 00265 if (oldFrameData->deref(this)) { 00266 const QString oldKey = d->cacheId(oldFrameData, oldPrefix); 00267 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00268 delete oldFrameData; 00269 } 00270 } 00271 } 00272 00273 d->location = Floating; 00274 } 00275 00276 bool FrameSvg::hasElementPrefix(const QString & prefix) const 00277 { 00278 //for now it simply checks if a center element exists, 00279 //because it could make sense for certain themes to not have all the elements 00280 if (prefix.isEmpty()) { 00281 return hasElement("center"); 00282 } else { 00283 return hasElement(prefix % "-center"); 00284 } 00285 } 00286 00287 bool FrameSvg::hasElementPrefix(Plasma::Location location) const 00288 { 00289 switch (location) { 00290 case TopEdge: 00291 return hasElementPrefix("north"); 00292 break; 00293 case BottomEdge: 00294 return hasElementPrefix("south"); 00295 break; 00296 case LeftEdge: 00297 return hasElementPrefix("west"); 00298 break; 00299 case RightEdge: 00300 return hasElementPrefix("east"); 00301 break; 00302 default: 00303 return hasElementPrefix(QString()); 00304 break; 00305 } 00306 } 00307 00308 QString FrameSvg::prefix() 00309 { 00310 if (d->prefix.isEmpty()) { 00311 return d->prefix; 00312 } 00313 00314 return d->prefix.left(d->prefix.size() - 1); 00315 } 00316 00317 void FrameSvg::resizeFrame(const QSizeF &size) 00318 { 00319 if (imagePath().isEmpty()) { 00320 return; 00321 } 00322 00323 if (size.isEmpty()) { 00324 kWarning() << "Invalid size" << size; 00325 return; 00326 } 00327 00328 FrameData *fd = d->frames[d->prefix]; 00329 if (size == fd->frameSize) { 00330 return; 00331 } 00332 00333 const QString oldKey = d->cacheId(fd, d->prefix); 00334 const QSize currentSize = fd->frameSize; 00335 fd->frameSize = size.toSize(); 00336 const QString newKey = d->cacheId(fd, d->prefix); 00337 fd->frameSize = currentSize; 00338 00339 //kDebug() << "looking for" << newKey; 00340 FrameData *newFd = FrameSvgPrivate::s_sharedFrames.value(newKey); 00341 if (newFd) { 00342 //kDebug() << "FOUND IT!" << newFd->refcount; 00343 // we've found a math, so insert that new one and ref it .. 00344 newFd->ref(this); 00345 d->frames.insert(d->prefix, newFd); 00346 00347 //.. then deref the old one and if it's no longer used, get rid of it 00348 if (fd->deref(this)) { 00349 //const QString oldKey = d->cacheId(fd, d->prefix); 00350 //kDebug() << "1. Removing it" << oldKey << fd->refcount; 00351 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00352 delete fd; 00353 } 00354 00355 return; 00356 } 00357 00358 if (fd->refcount() == 1) { 00359 // we're the only user of it, let's remove it from the shared keys 00360 // we don't want to deref it, however, as we'll still be using it 00361 FrameSvgPrivate::s_sharedFrames.remove(oldKey); 00362 } else { 00363 // others are using it, but we wish to change its size. so deref it, 00364 // then create a copy of it (we're automatically ref'd via the ctor), 00365 // then insert it into our frames. 00366 fd->deref(this); 00367 fd = new FrameData(*fd, this); 00368 d->frames.insert(d->prefix, fd); 00369 } 00370 00371 d->updateSizes(); 00372 fd->frameSize = size.toSize(); 00373 // we know it isn't in s_sharedFrames due to the check above, so insert it now 00374 FrameSvgPrivate::s_sharedFrames.insert(newKey, fd); 00375 } 00376 00377 QSizeF FrameSvg::frameSize() const 00378 { 00379 QHash<QString, FrameData*>::const_iterator it = d->frames.constFind(d->prefix); 00380 00381 if (it == d->frames.constEnd()) { 00382 return QSize(-1, -1); 00383 } else { 00384 return d->frameSize(it.value()); 00385 } 00386 } 00387 00388 qreal FrameSvg::marginSize(const Plasma::MarginEdge edge) const 00389 { 00390 if (d->frames[d->prefix]->noBorderPadding) { 00391 return .0; 00392 } 00393 00394 switch (edge) { 00395 case Plasma::TopMargin: 00396 return d->frames[d->prefix]->topMargin; 00397 break; 00398 00399 case Plasma::LeftMargin: 00400 return d->frames[d->prefix]->leftMargin; 00401 break; 00402 00403 case Plasma::RightMargin: 00404 return d->frames[d->prefix]->rightMargin; 00405 break; 00406 00407 //Plasma::BottomMargin 00408 default: 00409 return d->frames[d->prefix]->bottomMargin; 00410 break; 00411 } 00412 } 00413 00414 void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const 00415 { 00416 FrameData *frame = d->frames[d->prefix]; 00417 00418 if (frame->noBorderPadding) { 00419 left = top = right = bottom = 0; 00420 return; 00421 } 00422 00423 top = frame->topMargin; 00424 left = frame->leftMargin; 00425 right = frame->rightMargin; 00426 bottom = frame->bottomMargin; 00427 } 00428 00429 QRectF FrameSvg::contentsRect() const 00430 { 00431 QSizeF size(frameSize()); 00432 00433 if (size.isValid()) { 00434 QRectF rect(QPointF(0, 0), size); 00435 FrameData *frame = d->frames[d->prefix]; 00436 00437 return rect.adjusted(frame->leftMargin, frame->topMargin, 00438 -frame->rightMargin, -frame->bottomMargin); 00439 } else { 00440 return QRectF(); 00441 } 00442 } 00443 00444 QPixmap FrameSvg::alphaMask() const 00445 { 00446 //FIXME: the distinction between overlay and 00447 return d->alphaMask(); 00448 } 00449 00450 QRegion FrameSvg::mask() const 00451 { 00452 FrameData *frame = d->frames[d->prefix]; 00453 QString id = d->cacheId(frame, QString()); 00454 if (!frame->cachedMasks.contains(id)) { 00455 //TODO: Implement a better way to cap the number of cached masks 00456 if (frame->cachedMasks.count() > frame->MAX_CACHED_MASKS) { 00457 frame->cachedMasks.clear(); 00458 } 00459 frame->cachedMasks.insert(id, QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black)))); 00460 } 00461 return frame->cachedMasks[id]; 00462 } 00463 00464 void FrameSvg::setCacheAllRenderedFrames(bool cache) 00465 { 00466 if (d->cacheAll && !cache) { 00467 clearCache(); 00468 } 00469 00470 d->cacheAll = cache; 00471 } 00472 00473 bool FrameSvg::cacheAllRenderedFrames() const 00474 { 00475 return d->cacheAll; 00476 } 00477 00478 void FrameSvg::clearCache() 00479 { 00480 FrameData *frame = d->frames[d->prefix]; 00481 00482 // delete all the frames that aren't this one 00483 QMutableHashIterator<QString, FrameData*> it(d->frames); 00484 while (it.hasNext()) { 00485 FrameData *p = it.next().value(); 00486 if (frame != p) { 00487 //TODO: should we clear from the Theme pixmap cache as well? 00488 if (p->deref(this)) { 00489 const QString key = d->cacheId(p, it.key()); 00490 FrameSvgPrivate::s_sharedFrames.remove(key); 00491 p->cachedBackground = QPixmap(); 00492 } 00493 00494 it.remove(); 00495 } 00496 } 00497 } 00498 00499 QPixmap FrameSvg::framePixmap() 00500 { 00501 FrameData *frame = d->frames[d->prefix]; 00502 if (frame->cachedBackground.isNull()) { 00503 d->generateBackground(frame); 00504 if (frame->cachedBackground.isNull()) { 00505 return QPixmap(); 00506 } 00507 } 00508 00509 return frame->cachedBackground; 00510 } 00511 00512 void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) 00513 { 00514 FrameData *frame = d->frames[d->prefix]; 00515 if (frame->cachedBackground.isNull()) { 00516 d->generateBackground(frame); 00517 if (frame->cachedBackground.isNull()) { 00518 return; 00519 } 00520 } 00521 00522 painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target); 00523 } 00524 00525 void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) 00526 { 00527 FrameData *frame = d->frames[d->prefix]; 00528 if (frame->cachedBackground.isNull()) { 00529 d->generateBackground(frame); 00530 if (frame->cachedBackground.isNull()) { 00531 return; 00532 } 00533 } 00534 00535 painter->drawPixmap(pos, frame->cachedBackground); 00536 } 00537 00538 //#define DEBUG_FRAMESVG_CACHE 00539 FrameSvgPrivate::~FrameSvgPrivate() 00540 { 00541 #ifdef DEBUG_FRAMESVG_CACHE 00542 kDebug() << "*************" << q << q->imagePath() << "****************"; 00543 #endif 00544 00545 QHashIterator<QString, FrameData *> it(frames); 00546 while (it.hasNext()) { 00547 it.next(); 00548 if (it.value()) { 00549 // we remove all references from this widget to the frame, and delete it if we're the 00550 // last user 00551 if (it.value()->removeRefs(q)) { 00552 const QString key = cacheId(it.value(), it.key()); 00553 #ifdef DEBUG_FRAMESVG_CACHE 00554 kDebug() << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames.contains(key); 00555 #endif 00556 s_sharedFrames.remove(key); 00557 delete it.value(); 00558 } 00559 #ifdef DEBUG_FRAMESVG_CACHE 00560 else { 00561 kDebug() << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed(); 00562 } 00563 } else { 00564 kDebug() << "lost our value for" << it.key(); 00565 #endif 00566 } 00567 } 00568 00569 #ifdef DEBUG_FRAMESVG_CACHE 00570 QHashIterator<QString, FrameData *> it2(s_sharedFrames); 00571 int shares = 0; 00572 while (it2.hasNext()) { 00573 it2.next(); 00574 const int rc = it2.value()->refcount(); 00575 if (rc == 0) { 00576 kDebug() << " LOST!" << it2.key() << rc << it2.value();// << it2.value()->references; 00577 } else { 00578 kDebug() << " " << it2.key() << rc << it2.value(); 00579 foreach (FrameSvg *data, it2.value()->references.keys()) { 00580 kDebug( )<< " " << (void*)data << it2.value()->references[data]; 00581 } 00582 shares += rc - 1; 00583 } 00584 } 00585 kDebug() << "#####################################" << s_sharedFrames.count() << ", pixmaps saved:" << shares; 00586 #endif 00587 00588 frames.clear(); 00589 } 00590 00591 QPixmap FrameSvgPrivate::alphaMask() 00592 { 00593 FrameData *frame = frames[prefix]; 00594 QString maskPrefix; 00595 00596 if (q->hasElement("mask-" % prefix % "center")) { 00597 maskPrefix = "mask-"; 00598 } 00599 00600 if (maskPrefix.isNull()) { 00601 if (frame->cachedBackground.isNull()) { 00602 generateBackground(frame); 00603 if (frame->cachedBackground.isNull()) { 00604 return QPixmap(); 00605 } 00606 } 00607 00608 return frame->cachedBackground; 00609 } else { 00610 QString oldPrefix = prefix; 00611 00612 // We are setting the prefix only temporary to generate 00613 // the needed mask image 00614 prefix = maskPrefix % oldPrefix; 00615 00616 if (!frames.contains(prefix)) { 00617 const QString key = cacheId(frame, prefix); 00618 // see if we can find a suitable candidate in the shared frames 00619 // if successful, ref and insert, otherwise create a new one 00620 // and insert that into both the shared frames and our frames. 00621 FrameData *maskFrame = s_sharedFrames.value(key); 00622 00623 if (maskFrame) { 00624 maskFrame->ref(q); 00625 } else { 00626 maskFrame = new FrameData(*frame, q); 00627 s_sharedFrames.insert(key, maskFrame); 00628 } 00629 00630 frames.insert(prefix, maskFrame); 00631 updateSizes(); 00632 } 00633 00634 FrameData *maskFrame = frames[prefix]; 00635 if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) { 00636 const QString oldKey = cacheId(maskFrame, prefix); 00637 maskFrame->frameSize = frameSize(frame).toSize(); 00638 const QString newKey = cacheId(maskFrame, prefix); 00639 if (s_sharedFrames.contains(oldKey)) { 00640 s_sharedFrames.remove(oldKey); 00641 s_sharedFrames.insert(newKey, maskFrame); 00642 } 00643 00644 maskFrame->cachedBackground = QPixmap(); 00645 00646 generateBackground(maskFrame); 00647 if (maskFrame->cachedBackground.isNull()) { 00648 return QPixmap(); 00649 } 00650 } 00651 00652 prefix = oldPrefix; 00653 return maskFrame->cachedBackground; 00654 } 00655 } 00656 00657 void FrameSvgPrivate::generateBackground(FrameData *frame) 00658 { 00659 if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) { 00660 return; 00661 } 00662 00663 const QString id = cacheId(frame, prefix); 00664 00665 Theme *theme = Theme::defaultTheme(); 00666 bool frameCached = !frame->cachedBackground.isNull(); 00667 bool overlayCached = false; 00668 const bool overlayAvailable = !prefix.startsWith("mask-") && q->hasElement(prefix % "overlay"); 00669 QPixmap overlay; 00670 if (q->isUsingRenderingCache()) { 00671 frameCached = theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); 00672 00673 if (overlayAvailable) { 00674 overlayCached = theme->findInCache("overlay_" % id, overlay) && !overlay.isNull(); 00675 } 00676 } 00677 00678 if (!frameCached) { 00679 generateFrameBackground(frame); 00680 } 00681 00682 //Overlays 00683 QSize overlaySize; 00684 QPoint actualOverlayPos = QPoint(0, 0); 00685 if (overlayAvailable && !overlayCached) { 00686 QPoint pos = QPoint(0, 0); 00687 overlaySize = q->elementSize(prefix % "overlay"); 00688 00689 //Random pos, stretched and tiled are mutually exclusive 00690 if (q->hasElement(prefix % "hint-overlay-random-pos")) { 00691 actualOverlayPos = overlayPos; 00692 } else if (q->hasElement(prefix % "hint-overlay-pos-right")) { 00693 actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); 00694 } else if (q->hasElement(prefix % "hint-overlay-pos-bottom")) { 00695 actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); 00696 //Stretched or Tiled? 00697 } else if (q->hasElement(prefix % "hint-overlay-stretch")) { 00698 overlaySize = frameSize(frame).toSize(); 00699 } else { 00700 if (q->hasElement(prefix % "hint-overlay-tile-horizontal")) { 00701 overlaySize.setWidth(frameSize(frame).width()); 00702 } 00703 if (q->hasElement(prefix % "hint-overlay-tile-vertical")) { 00704 overlaySize.setHeight(frameSize(frame).height()); 00705 } 00706 } 00707 00708 overlay = alphaMask(); 00709 QPainter overlayPainter(&overlay); 00710 overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); 00711 //Tiling? 00712 if (q->hasElement(prefix % "hint-overlay-tile-horizontal") || 00713 q->hasElement(prefix % "hint-overlay-tile-vertical")) { 00714 00715 QSize s = q->size(); 00716 q->resize(q->elementSize(prefix % "overlay")); 00717 00718 overlayPainter.drawTiledPixmap(QRect(QPoint(0,0), overlaySize), q->pixmap(prefix % "overlay")); 00719 q->resize(s); 00720 } else { 00721 q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % "overlay"); 00722 } 00723 00724 overlayPainter.end(); 00725 } 00726 00727 if (!frameCached) { 00728 cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); 00729 } 00730 00731 if (!overlay.isNull()) { 00732 QPainter p(&frame->cachedBackground); 00733 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 00734 p.drawPixmap(actualOverlayPos, overlay, QRect(actualOverlayPos, overlaySize)); 00735 } 00736 } 00737 00738 void FrameSvgPrivate::generateFrameBackground(FrameData *frame) 00739 { 00740 //kDebug() << "generating background"; 00741 const QSizeF size = frameSize(frame); 00742 const int topWidth = q->elementSize(prefix % "top").width(); 00743 const int leftHeight = q->elementSize(prefix % "left").height(); 00744 const int topOffset = 0; 00745 const int leftOffset = 0; 00746 00747 00748 if (!size.isValid()) { 00749 kWarning() << "Invalid frame size" << size; 00750 return; 00751 } 00752 if (size.width() >= MAX_FRAME_SIZE || size.height() >= MAX_FRAME_SIZE) { 00753 kWarning() << "Not generating frame background for a size whose width or height is more than" << MAX_FRAME_SIZE << size; 00754 return; 00755 } 00756 00757 const int contentWidth = size.width() - frame->leftWidth - frame->rightWidth; 00758 const int contentHeight = size.height() - frame->topHeight - frame->bottomHeight; 00759 int contentTop = 0; 00760 int contentLeft = 0; 00761 int rightOffset = contentWidth; 00762 int bottomOffset = contentHeight; 00763 00764 frame->cachedBackground = QPixmap(frame->leftWidth + contentWidth + frame->rightWidth, 00765 frame->topHeight + contentHeight + frame->bottomHeight); 00766 frame->cachedBackground.fill(Qt::transparent); 00767 QPainter p(&frame->cachedBackground); 00768 p.setCompositionMode(QPainter::CompositionMode_Source); 00769 p.setRenderHint(QPainter::SmoothPixmapTransform); 00770 00771 //CENTER 00772 if (frame->tileCenter) { 00773 if (contentHeight > 0 && contentWidth > 0) { 00774 const int centerTileHeight = q->elementSize(prefix % "center").height(); 00775 const int centerTileWidth = q->elementSize(prefix % "center").width(); 00776 QPixmap center(centerTileWidth, centerTileHeight); 00777 center.fill(Qt::transparent); 00778 00779 { 00780 QPainter centerPainter(¢er); 00781 centerPainter.setCompositionMode(QPainter::CompositionMode_Source); 00782 q->paint(¢erPainter, QRect(QPoint(0, 0), q->elementSize(prefix % "center")), prefix % "center"); 00783 } 00784 00785 if (frame->composeOverBorder) { 00786 p.drawTiledPixmap(QRect(QPoint(0, 0), size.toSize()), center); 00787 } else { 00788 p.drawTiledPixmap(QRect(frame->leftWidth, frame->topHeight, 00789 contentWidth, contentHeight), center); 00790 } 00791 } 00792 } else { 00793 if (contentHeight > 0 && contentWidth > 0) { 00794 if (frame->composeOverBorder) { 00795 q->paint(&p, QRect(QPoint(0, 0), size.toSize()), 00796 prefix % "center"); 00797 } else { 00798 q->paint(&p, QRect(frame->leftWidth, frame->topHeight, 00799 contentWidth, contentHeight), 00800 prefix % "center"); 00801 } 00802 } 00803 } 00804 00805 if (frame->composeOverBorder) { 00806 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 00807 p.drawPixmap(QRect(QPoint(0, 0), size.toSize()), alphaMask()); 00808 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 00809 } 00810 00811 if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left")) { 00812 rightOffset += frame->leftWidth; 00813 } 00814 00815 // Corners 00816 if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) { 00817 contentTop = frame->topHeight; 00818 bottomOffset += frame->topHeight; 00819 00820 if (q->hasElement(prefix % "topleft") && frame->enabledBorders & FrameSvg::LeftBorder) { 00821 q->paint(&p, QRect(leftOffset, topOffset, frame->leftWidth, frame->topHeight), prefix % "topleft"); 00822 00823 contentLeft = frame->leftWidth; 00824 } 00825 00826 if (q->hasElement(prefix % "topright") && frame->enabledBorders & FrameSvg::RightBorder) { 00827 q->paint(&p, QRect(rightOffset, topOffset, frame->rightWidth, frame->topHeight), prefix % "topright"); 00828 } 00829 } 00830 00831 if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) { 00832 if (q->hasElement(prefix % "bottomleft") && frame->enabledBorders & FrameSvg::LeftBorder) { 00833 q->paint(&p, QRect(leftOffset, bottomOffset, frame->leftWidth, frame->bottomHeight), prefix % "bottomleft"); 00834 00835 contentLeft = frame->leftWidth; 00836 } 00837 00838 if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "bottomright")) { 00839 q->paint(&p, QRect(rightOffset, bottomOffset, frame->rightWidth, frame->bottomHeight), prefix % "bottomright"); 00840 } 00841 } 00842 00843 // Sides 00844 if (frame->stretchBorders) { 00845 if (frame->enabledBorders & FrameSvg::LeftBorder || frame->enabledBorders & FrameSvg::RightBorder) { 00846 if (q->hasElement(prefix % "left") && 00847 frame->enabledBorders & FrameSvg::LeftBorder) { 00848 q->paint(&p, QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), prefix % "left"); 00849 } 00850 00851 if (q->hasElement(prefix % "right") && 00852 frame->enabledBorders & FrameSvg::RightBorder) { 00853 q->paint(&p, QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), prefix % "right"); 00854 } 00855 } 00856 00857 if (frame->enabledBorders & FrameSvg::TopBorder || frame->enabledBorders & FrameSvg::BottomBorder) { 00858 if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top")) { 00859 q->paint(&p, QRect(contentLeft, topOffset, contentWidth, frame->topHeight), prefix % "top"); 00860 } 00861 00862 if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom")) { 00863 q->paint(&p, QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), prefix % "bottom"); 00864 } 00865 } 00866 } else { 00867 if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % "left") 00868 && leftHeight > 0 && frame->leftWidth > 0) { 00869 QPixmap left(frame->leftWidth, leftHeight); 00870 left.fill(Qt::transparent); 00871 00872 QPainter sidePainter(&left); 00873 sidePainter.setCompositionMode(QPainter::CompositionMode_Source); 00874 q->paint(&sidePainter, QRect(QPoint(0, 0), left.size()), prefix % "left"); 00875 00876 p.drawTiledPixmap(QRect(leftOffset, contentTop, frame->leftWidth, contentHeight), left); 00877 } 00878 00879 if (frame->enabledBorders & FrameSvg::RightBorder && q->hasElement(prefix % "right") && 00880 leftHeight > 0 && frame->rightWidth > 0) { 00881 QPixmap right(frame->rightWidth, leftHeight); 00882 right.fill(Qt::transparent); 00883 00884 QPainter sidePainter(&right); 00885 sidePainter.setCompositionMode(QPainter::CompositionMode_Source); 00886 q->paint(&sidePainter, QRect(QPoint(0, 0), right.size()), prefix % "right"); 00887 00888 p.drawTiledPixmap(QRect(rightOffset, contentTop, frame->rightWidth, contentHeight), right); 00889 } 00890 00891 if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % "top") 00892 && topWidth > 0 && frame->topHeight > 0) { 00893 QPixmap top(topWidth, frame->topHeight); 00894 top.fill(Qt::transparent); 00895 00896 QPainter sidePainter(&top); 00897 sidePainter.setCompositionMode(QPainter::CompositionMode_Source); 00898 q->paint(&sidePainter, QRect(QPoint(0, 0), top.size()), prefix % "top"); 00899 00900 p.drawTiledPixmap(QRect(contentLeft, topOffset, contentWidth, frame->topHeight), top); 00901 } 00902 00903 if (frame->enabledBorders & FrameSvg::BottomBorder && q->hasElement(prefix % "bottom") 00904 && topWidth > 0 && frame->bottomHeight > 0) { 00905 QPixmap bottom(topWidth, frame->bottomHeight); 00906 bottom.fill(Qt::transparent); 00907 00908 QPainter sidePainter(&bottom); 00909 sidePainter.setCompositionMode(QPainter::CompositionMode_Source); 00910 q->paint(&sidePainter, QRect(QPoint(0, 0), bottom.size()), prefix % "bottom"); 00911 00912 p.drawTiledPixmap(QRect(contentLeft, bottomOffset, contentWidth, frame->bottomHeight), bottom); 00913 } 00914 } 00915 00916 } 00917 00918 QString FrameSvgPrivate::cacheId(FrameData *frame, const QString &prefixToSave) const 00919 { 00920 const QSize size = frameSize(frame).toSize(); 00921 const QLatin1Char s('_'); 00922 return QString::number(frame->enabledBorders) % s % QString::number(size.width()) % s % QString::number(size.height()) % s % prefixToSave % s % q->imagePath(); 00923 } 00924 00925 void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay) 00926 { 00927 if (!q->isUsingRenderingCache()) { 00928 return; 00929 } 00930 00931 //insert background 00932 FrameData *frame = frames.value(prefixToSave); 00933 00934 if (!frame) { 00935 return; 00936 } 00937 00938 const QString id = cacheId(frame, prefixToSave); 00939 00940 //kDebug()<<"Saving to cache frame"<<id; 00941 00942 Theme::defaultTheme()->insertIntoCache(id, background, QString::number((qint64)q, 16) % prefixToSave); 00943 00944 if (!overlay.isNull()) { 00945 //insert overlay 00946 Theme::defaultTheme()->insertIntoCache("overlay_" % id, overlay, QString::number((qint64)q, 16) % prefixToSave % "overlay"); 00947 } 00948 } 00949 00950 void FrameSvgPrivate::updateSizes() const 00951 { 00952 //kDebug() << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; 00953 FrameData *frame = frames[prefix]; 00954 Q_ASSERT(frame); 00955 00956 QSize s = q->size(); 00957 q->resize(); 00958 frame->cachedBackground = QPixmap(); 00959 00960 if (frame->enabledBorders & FrameSvg::TopBorder) { 00961 frame->topHeight = q->elementSize(prefix % "top").height(); 00962 00963 if (q->hasElement(prefix % "hint-top-margin")) { 00964 frame->topMargin = q->elementSize(prefix % "hint-top-margin").height(); 00965 } else { 00966 frame->topMargin = frame->topHeight; 00967 } 00968 } else { 00969 frame->topMargin = frame->topHeight = 0; 00970 } 00971 00972 if (frame->enabledBorders & FrameSvg::LeftBorder) { 00973 frame->leftWidth = q->elementSize(prefix % "left").width(); 00974 00975 if (q->hasElement(prefix % "hint-left-margin")) { 00976 frame->leftMargin = q->elementSize(prefix % "hint-left-margin").width(); 00977 } else { 00978 frame->leftMargin = frame->leftWidth; 00979 } 00980 } else { 00981 frame->leftMargin = frame->leftWidth = 0; 00982 } 00983 00984 if (frame->enabledBorders & FrameSvg::RightBorder) { 00985 frame->rightWidth = q->elementSize(prefix % "right").width(); 00986 00987 if (q->hasElement(prefix % "hint-right-margin")) { 00988 frame->rightMargin = q->elementSize(prefix % "hint-right-margin").width(); 00989 } else { 00990 frame->rightMargin = frame->rightWidth; 00991 } 00992 } else { 00993 frame->rightMargin = frame->rightWidth = 0; 00994 } 00995 00996 if (frame->enabledBorders & FrameSvg::BottomBorder) { 00997 frame->bottomHeight = q->elementSize(prefix % "bottom").height(); 00998 00999 if (q->hasElement(prefix % "hint-bottom-margin")) { 01000 frame->bottomMargin = q->elementSize(prefix % "hint-bottom-margin").height(); 01001 } else { 01002 frame->bottomMargin = frame->bottomHeight; 01003 } 01004 } else { 01005 frame->bottomMargin = frame->bottomHeight = 0; 01006 } 01007 01008 frame->composeOverBorder = (q->hasElement(prefix % "hint-compose-over-border") && 01009 q->hasElement(prefix % "mask-center")); 01010 01011 //since it's rectangular, topWidth and bottomWidth must be the same 01012 //the ones that don't have a prefix is for retrocompatibility 01013 frame->tileCenter = q->hasElement("hint-tile-center"); 01014 frame->noBorderPadding = q->hasElement("hint-no-border-padding"); 01015 frame->stretchBorders = q->hasElement("hint-stretch-borders"); 01016 q->resize(s); 01017 } 01018 01019 void FrameSvgPrivate::updateNeeded() 01020 { 01021 q->clearCache(); 01022 updateSizes(); 01023 } 01024 01025 void FrameSvgPrivate::updateAndSignalSizes() 01026 { 01027 updateSizes(); 01028 emit q->repaintNeeded(); 01029 } 01030 01031 QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const 01032 { 01033 if (!frame->frameSize.isValid()) { 01034 updateSizes(); 01035 frame->frameSize = q->size(); 01036 } 01037 01038 return frame->frameSize; 01039 } 01040 01041 void FrameData::ref(FrameSvg *svg) 01042 { 01043 references[svg] = references[svg] + 1; 01044 //kDebug() << this << svg << references[svg]; 01045 } 01046 01047 bool FrameData::deref(FrameSvg *svg) 01048 { 01049 references[svg] = references[svg] - 1; 01050 //kDebug() << this << svg << references[svg]; 01051 if (references[svg] < 1) { 01052 references.remove(svg); 01053 } 01054 01055 return references.isEmpty(); 01056 } 01057 01058 bool FrameData::removeRefs(FrameSvg *svg) 01059 { 01060 references.remove(svg); 01061 return references.isEmpty(); 01062 } 01063 01064 bool FrameData::isUsed() const 01065 { 01066 return !references.isEmpty(); 01067 } 01068 01069 int FrameData::refcount() const 01070 { 01071 return references.count(); 01072 } 01073 01074 } // Plasma namespace 01075 01076 #include "framesvg.moc"
KDE 4.6 API Reference