18 #include <kstandarddirs.h>
21 #include <QtConcurrentRun>
25 #include <QApplication>
27 using namespace Practice;
30 :
QObject(parent), m_haveCache(true), m_queuedRequest(false), m_isFastScaledRender(true)
33 m_cache.
setSaveFilename(KStandardDirs::locateLocal(
"appdata", cacheFilename));
34 m_timer.setSingleShot(
true);
35 m_timer.setInterval(1000);
42 if (m_future.isRunning()) {
43 kDebug() <<
"Waiting for rendering to finish";
44 m_future.waitForFinished();
52 if(!m_theme->
load(theme)) {
53 kDebug() <<
"could not load theme" << theme;
55 m_renderer.load(m_theme->
graphics());
57 m_haveCache = !m_cache.
isEmpty();
58 m_lastScaledRenderRects.clear();
59 m_lastFullRenderRects.clear();
60 m_rectMappings.clear();
66 m_rectMappings.clear();
71 m_rects.append(qMakePair<QString, QRect>(name, rect));
72 if (!m_rectMappings.contains(name)) {
73 QString mapped = m_theme->
property(
"X-Parley-"+name);
74 m_rectMappings[name] = mapped.isEmpty() ? name : mapped;
80 if (m_rects.isEmpty() || m_rects[0].second.isEmpty()) {
83 if (m_future.isRunning() || m_future.resultCount()) {
90 if (m_lastScaledRenderRects == m_rects) {
95 QFutureWatcher<QImage> watcher;
96 m_future = QtConcurrent::run(
this, &ThemedBackgroundRenderer::renderBackground,
true);
97 watcher.setFuture(m_future);
98 watcher.waitForFinished();
100 QPixmap result = QPixmap::fromImage(m_future.result());
101 m_future = QFuture<QImage>();
102 m_lastScaledRenderRects = m_rects;
108 QString text = m_theme->
property(
"X-Parley-Font-Color-"+context).toLower();
109 if (text.length() == 6 && text.contains(QRegExp(
"[0-9a-f]{6}"))) {
110 return QColor(text.mid(0,2).toInt(0,16),
111 text.mid(2,2).toInt(0,16),
112 text.mid(4,2).toInt(0,16));
121 if (m_rects.isEmpty() || m_rects[0].second.isEmpty()) {
130 bool fastScale =
false;
131 if (m_future.isRunning()) {
135 if (m_lastFullRenderRects == m_rects && m_lastScaledRenderRects == m_rects) {
139 m_future = QtConcurrent::run(
this, &ThemedBackgroundRenderer::renderBackground, fastScale);
140 m_watcher.setFuture(m_future);
141 m_lastFullRenderRects = m_rects;
146 if(!m_future.resultCount()) {
151 m_future = QFuture<QImage>();
156 if (!m_renderer.elementExists(
id))
158 return m_renderer.boundsOnElement(
id).size();
163 if (!m_renderer.elementExists(
id))
165 return m_renderer.boundsOnElement(
id);
170 if (!m_renderer.elementExists(
id))
172 QRectF itemRect = m_renderer.boundsOnElement(
id);
173 if (itemRect.isNull())
176 size = itemRect.size().toSize();
179 QImage image(size, QImage::Format_ARGB32_Premultiplied);
180 image.fill(QColor(Qt::transparent).rgba());
182 m_renderer.render(&p,
id, QRectF(QPointF(0,0), size));
184 return QPixmap::fromImage(image);
186 return QPixmap::fromImage(m_cache.
getImage(
id));
193 if (!m_rects.empty()) {
194 rect = m_rects.at(0).first;
196 if (m_rectMappings.contains(rect)) {
197 rect = m_rectMappings.value(rect);
200 if (m_renderer.elementExists(rect+
"-border-topleft"))
201 margins.setTop(m_renderer.boundsOnElement(rect+
"-border-topleft").toAlignedRect().height());
202 if (m_renderer.elementExists(rect+
"-border-bottomleft"))
203 margins.setBottom(m_renderer.boundsOnElement(rect+
"-border-bottomleft").toAlignedRect().height());
204 if (m_renderer.elementExists(rect+
"-border-topleft"))
205 margins.setLeft(m_renderer.boundsOnElement(rect+
"-border-topleft").toAlignedRect().width());
206 if (m_renderer.elementExists(rect+
"-border-topright"))
207 margins.setRight(m_renderer.boundsOnElement(rect+
"-border-topright").toAlignedRect().width());
211 QImage ThemedBackgroundRenderer::renderBackground(
bool fastScale)
213 m_isFastScaledRender =
false;
215 QTime t = QTime::currentTime();
216 QImage image(m_rects[0].second.size(), QImage::Format_ARGB32_Premultiplied);
217 image.fill(QColor(Qt::transparent).rgba());
220 QPair<QString, QRect> rect;
221 Q_FOREACH(rect, m_rects) {
222 if (!m_rects.isEmpty() && rect == m_rects[0]) {
224 rect.second = QRect(QPoint(margins.left(),margins.top()), rect.second.size()-QSize(margins.right()+margins.left(), margins.bottom()+margins.top()));
226 renderRect(rect.first, rect.second, &p, fastScale);
233 void ThemedBackgroundRenderer::renderRect(
const QString& name,
const QRect& rect, QPainter *p,
bool fastScale)
235 renderItem(name,
"center", rect, p, fastScale,
Rect, Qt::IgnoreAspectRatio,
Center,
Centered,
true);
236 renderItem(name,
"center-ratio", rect, p, fastScale,
Rect, Qt::IgnoreAspectRatio,
Center,
Centered,
true);
237 renderItem(name,
"center-noscale", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio,
Center,
Centered,
true);
239 renderItem(name,
"border-topleft", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio,
Top,
Corner,
false);
240 renderItem(name,
"border-topright", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio,
Right,
Corner,
false);
241 renderItem(name,
"border-bottomleft", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio,
Left,
Corner,
false);
242 renderItem(name,
"border-bottomright", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio,
Bottom,
Corner,
false);
245 edges <<
"top" <<
"bottom" <<
"left" <<
"right";
246 Q_FOREACH(
const QString& edge, edges) {
249 if(edge == QLatin1String(
"top")) {
252 }
else if(edge == QLatin1String(
"bottom")) {
255 }
else if(edge == QLatin1String(
"right")) {
262 for(
int inside = 1; inside>=0; inside--) {
263 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge, rect, p, fastScale, scaleBase, Qt::IgnoreAspectRatio, alignEdge,
Centered, inside);
264 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge+
"-ratio", rect, p, fastScale, scaleBase, Qt::KeepAspectRatio, alignEdge,
Centered, inside);
265 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge+
"-noscale", rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio, alignEdge,
Centered, inside);
266 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge+
"-repeat", rect, p, fastScale, scaleBase, Qt::IgnoreAspectRatio, alignEdge,
Repeated, inside);
267 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge+
"-"+(scaleBase==
Vertical?
"top":
"left"), rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio, alignEdge,
LeftTop, inside);
268 renderItem(name, QString(inside?
"inside":
"border")+
"-"+edge+
"-"+(scaleBase==
Vertical?
"bottom":
"right"), rect, p, fastScale,
NoScale, Qt::IgnoreAspectRatio, alignEdge,
RightBottom, inside);
273 void ThemedBackgroundRenderer::renderItem(
const QString& idBase,
const QString& idSuffix,
const QRect& rect, QPainter *p,
bool fastScale, ScaleBase scaleBase, Qt::AspectRatioMode aspectRatio, Edge edge, Align align,
bool inside)
277 QString
id = idBase+
'-'+idSuffix;
279 QString mappedId = m_rectMappings.contains(idBase)? m_rectMappings.value(idBase)+
'-'+idSuffix : id;
281 if (!m_renderer.elementExists(mappedId))
283 QRectF itemRectF = m_renderer.boundsOnElement(mappedId);
284 if (itemRectF.isNull() || rect.isNull())
289 QRect itemRect = scaleRect(itemRectF, rect, scaleBase, aspectRatio);
291 itemRect = alignRect(itemRect, rect, edge, align, inside);
295 if (m_cache.
imageSize(
id) == itemRect.size()) {
298 }
else if(fastScale && !m_cache.
imageSize(
id).isEmpty()) {
300 image = m_cache.
getImage(
id).scaled(itemRect.size(), Qt::IgnoreAspectRatio, Qt::FastTransformation);
301 m_isFastScaledRender =
true;
304 image = QImage(itemRect.size(), QImage::Format_ARGB32_Premultiplied);
305 image.fill(QColor(Qt::transparent).rgba());
306 QPainter painter(&image);
308 QImage tile(itemRectF.toRect().size(), QImage::Format_ARGB32_Premultiplied);
309 tile.fill(QColor(Qt::transparent).rgba());
310 QPainter tilePainter(&tile);
311 m_renderer.render(&tilePainter, mappedId, QRect(QPoint(0, 0), tile.size()));
312 painter.fillRect(image.rect(), QBrush(tile));
313 }
else if(aspectRatio == Qt::KeepAspectRatioByExpanding) {
314 m_renderer.render(&painter, mappedId, QRect(QPoint(0, 0), itemRect.size()));
316 QRect croppedRect = rect;
317 croppedRect.moveCenter(itemRect.center());
318 image = image.copy(croppedRect);
320 m_renderer.render(&painter, mappedId, QRect(QPoint(0, 0), itemRect.size()));
325 p->drawImage(itemRect.topLeft(), image);
328 QRect ThemedBackgroundRenderer::scaleRect(QRectF itemRect,
const QRect& baseRect, ScaleBase scaleBase, Qt::AspectRatioMode aspectRatio)
330 qreal verticalFactor = 0;
331 qreal horizontalFactor = 0;
334 return itemRect.toRect();
336 switch (aspectRatio) {
337 case Qt::IgnoreAspectRatio:
338 itemRect.setWidth(baseRect.width());
339 return itemRect.toRect();
340 case Qt::KeepAspectRatio:
341 horizontalFactor = baseRect.width()/itemRect.width();
342 itemRect.setWidth(baseRect.width());
343 itemRect.setHeight(itemRect.height()*horizontalFactor);
344 return itemRect.toRect();
345 case Qt::KeepAspectRatioByExpanding:
346 kWarning() <<
"KeepAspectRatioByExpanding only works for the center";
347 return itemRect.toRect();
351 switch (aspectRatio) {
352 case Qt::IgnoreAspectRatio:
353 itemRect.setHeight(baseRect.height());
354 return itemRect.toRect();
355 case Qt::KeepAspectRatio:
356 verticalFactor = baseRect.height()/itemRect.height();
357 itemRect.setHeight(baseRect.height());
358 itemRect.setWidth(itemRect.width()*verticalFactor);
359 return itemRect.toRect();
360 case Qt::KeepAspectRatioByExpanding:
361 kWarning() <<
"KeepAspectRatioByExpanding only works for the center";
362 return itemRect.toRect();
366 switch (aspectRatio) {
367 case Qt::IgnoreAspectRatio:
368 itemRect.setWidth(baseRect.width());
369 itemRect.setHeight(baseRect.height());
370 return itemRect.toRect();
371 case Qt::KeepAspectRatio:
372 horizontalFactor = baseRect.width()/itemRect.width();
373 verticalFactor = baseRect.height()/itemRect.height();
374 if (verticalFactor < horizontalFactor) {
375 itemRect.setHeight(baseRect.height());
376 itemRect.setWidth(itemRect.width()*verticalFactor);
378 itemRect.setWidth(baseRect.width());
379 itemRect.setHeight(itemRect.height()*horizontalFactor);
381 return itemRect.toRect();
382 case Qt::KeepAspectRatioByExpanding:
383 horizontalFactor = baseRect.width()/itemRect.width();
384 verticalFactor = baseRect.height()/itemRect.height();
385 if (verticalFactor > horizontalFactor) {
386 itemRect.setHeight(baseRect.height());
387 itemRect.setWidth(itemRect.width()*verticalFactor);
389 itemRect.setWidth(baseRect.width());
390 itemRect.setHeight(itemRect.height()*horizontalFactor);
392 return itemRect.toRect();
397 return itemRect.toRect();
400 QRect ThemedBackgroundRenderer::alignRect(QRect itemRect,
const QRect &baseRect, Edge edge, Align align,
bool inside)
403 int x = baseRect.x() + (baseRect.width()-itemRect.width())/2;
404 int y = baseRect.y() + (baseRect.height()-itemRect.height())/2;
405 itemRect.moveTo(x, y);
415 x = baseRect.x() - itemRect.width();
417 x = baseRect.x() + baseRect.width();
425 x = baseRect.x() + (baseRect.width()-itemRect.width())/2;
428 x = baseRect.x() + baseRect.width() - itemRect.width();
432 int y = baseRect.y();
434 y += baseRect.height()-itemRect.height();
436 if ((!inside) && edge ==
Top) {
437 y -= itemRect.height();
439 y += itemRect.height();
441 itemRect.moveTo(x, y);
443 }
else if (edge ==
Left || edge ==
Right) {
449 y = baseRect.y() - itemRect.height();
451 y = baseRect.y() + baseRect.height();
459 y = baseRect.y() + (baseRect.height()-itemRect.height())/2;
462 y = baseRect.y() + baseRect.height() - itemRect.height();
466 int x = baseRect.x();
468 x += baseRect.width()-itemRect.width();
470 if ((!inside) && edge ==
Left) {
471 x -= itemRect.width();
472 }
else if (!inside) {
473 x += itemRect.width();
475 itemRect.moveTo(x, y);
482 #include "themedbackgroundrenderer.moc"
void setSaveFilename(const QString &filename)
void updateBackgroundTimeout()
QPixmap getScaledBackground()
Class for loading theme files.
QMargins contentMargins()
virtual QString graphics() const
QColor fontColor(const QString &context, const QColor &fallback)
void addRect(const QString &name, const QRect &rect)
QSizeF getSizeForId(const QString &id)
QRectF getRectForId(const QString &id)
virtual bool load(const QString &file)
Load a specific theme file.
QSize imageSize(const QString &id)
void updateImage(const QString &id, const QImage &image)
void setTheme(const QString &theme)
QImage getImage(const QString &id)
ThemedBackgroundRenderer(QObject *parent, const QString &cacheFilename)
void backgroundChanged(QPixmap pixmap)
QPixmap getPixmapForId(const QString &id, QSize size=QSize())
~ThemedBackgroundRenderer()
void setFilenames(const QStringList &filename)
QString property(const QString &key) const