2
* Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
3
* Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
4
* Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
5
* Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include "kdmpixmap.h"
23
#include "kdmthemer.h"
25
#include <kstandarddirs.h>
27
#include <QDirIterator>
29
#include <QSignalMapper>
30
#include <QSvgRenderer>
34
KdmPixmap::KdmPixmap(QObject *parent, const QDomNode &node)
35
: KdmItem(parent, node)
42
// Set default values for pixmap (note: strings are already Null)
43
pixmap.normal.tint.setRgb(0xFFFFFF);
44
pixmap.normal.present = true;
47
QDomNodeList childList = node.childNodes();
48
for (int nod = 0; nod < childList.count(); nod++) {
49
QDomNode child = childList.item(nod);
50
QDomElement el = child.toElement();
51
QString tagName = el.tagName();
53
if (tagName == "normal") {
54
definePixmap(el, pixmap.normal);
55
parseColor(el.attribute("tint", "#ffffff"), el.attribute("alpha", "1.0"), pixmap.normal.tint);
56
} else if (tagName == "active") {
57
pixmap.active.present = true;
58
definePixmap(el, pixmap.active);
59
parseColor(el.attribute("tint", "#ffffff"), el.attribute("alpha", "1.0"), pixmap.active.tint);
60
} else if (tagName == "prelight") {
61
pixmap.prelight.present = true;
62
definePixmap(el, pixmap.prelight);
63
parseColor(el.attribute("tint", "#ffffff"), el.attribute("alpha", "1.0"), pixmap.prelight.tint);
69
KdmPixmap::definePixmap(const QDomElement &el, PixmapStruct::PixmapClass &pClass)
71
QString fileName = el.attribute("file");
72
if (!fileName.isEmpty()) {
73
if (fileName.at(0) != '/')
74
fileName.prepend(themer()->baseDir() + '/');
75
pClass.fullpath = fileName;
77
fileName = el.attribute("wallpaper");
78
if (fileName.isEmpty())
80
QString xf = KStandardDirs::locate("wallpaper", fileName + "/contents/images/");
82
pClass.package = true;
84
xf = KStandardDirs::locate("wallpaper", fileName);
86
kWarning() << "Cannot find wallpaper" << fileName;
93
QString aspect = el.attribute("scalemode", "free");
95
(aspect == "fit") ? Qt::KeepAspectRatio :
96
(aspect == "crop") ? Qt::KeepAspectRatioByExpanding :
97
Qt::IgnoreAspectRatio;
99
pClass.svgImage = fileName.endsWith(".svg") || fileName.endsWith(".svgz");
101
pClass.svgElement = el.attribute("element");
105
KdmPixmap::findBestPixmap(const QString &dir, const QString &pat,
106
const QRect &area, Qt::AspectRatioMode aspectMode)
108
int tw = area.width(), th = area.height();
109
float tar = 1.0 * tw / th;
112
float bestPenalty = 0;
113
QRegExp rx(QRegExp::escape(dir) + pat);
114
QDirIterator dit(dir);
115
while (dit.hasNext()) {
116
QString fn = dit.next();
117
if (rx.exactMatch(fn)) {
118
int w = rx.cap(1).toInt(), h = rx.cap(2).toInt();
119
// This algorithm considers need for zooming and distortion of
120
// aspect ratio / discarded pixels / pixels left free. The weighting
121
// gives good results in the tested cases, but is pretty arbitrary
122
// by all practical means.
123
float ar = 1.0 * w / h;
125
float rawAspect = ((tar > ar) ? tar / ar : ar / tar);
126
float penalty = rawAspect - 1;
127
if (aspectMode != Qt::IgnoreAspectRatio) {
128
bool exp = (aspectMode == Qt::KeepAspectRatioByExpanding);
129
// Give an advantage to non-zooming cases.
130
if ((w == tw && (h > th) == exp) || (h == th && (w > tw) == exp))
134
// Dropped pixels don't contribute to the input area.
138
// This mode does not preserve aspect ratio, so give an
139
// advantage to pics with a better ratio to start with.
142
// Too small is worse than too big - within limits.
143
penalty += sqrt((tpn > pn) ? (tpn / pn - 1) * 2 : pn / tpn - 1);
145
if (best.isEmpty() || penalty < bestPenalty) {
147
bestPenalty = penalty;
155
KdmPixmap::loadPixmap(PixmapStruct::PixmapClass &pClass)
157
if (!pClass.image.isNull())
159
if (pClass.fullpath.isEmpty())
162
if (pClass.package) {
163
// Always find best fit from package.
164
fn = findBestPixmap(pClass.fullpath, "(\\d+)x(\\d+)\\.[^.]+",
165
area.isValid() ? area : QRect(0, 0, 1600, 1200),
168
if (area.isValid()) {
169
if (QFile::exists(pClass.fullpath)) {
170
// If base file exists, use only a perfect match.
171
int dot = pClass.fullpath.lastIndexOf('.');
172
fn = pClass.fullpath.left(dot);
173
fn += QString("-%1x%2").arg(area.width()).arg(area.height());
174
fn += pClass.fullpath.mid(dot);
175
if (!QFile::exists(fn))
176
fn = pClass.fullpath;
178
// Otherwise find best match.
179
int sep = pClass.fullpath.lastIndexOf('/');
180
int dot = pClass.fullpath.lastIndexOf('.');
182
dot = pClass.fullpath.length();
183
QString f = QRegExp::escape(pClass.fullpath.mid(sep + 1, dot - sep - 1));
184
f += "-(\\d+)x(\\d+)";
185
f += QRegExp::escape(pClass.fullpath.mid(dot));
186
fn = findBestPixmap(pClass.fullpath.left(sep + 1), f, area,
190
fn = pClass.fullpath;
193
if (!pClass.image.load(fn)) {
194
kWarning() << "failed to load" << fn;
195
pClass.fullpath.clear();
198
if (pClass.image.format() != QImage::Format_ARGB32)
199
pClass.image = pClass.image.convertToFormat(QImage::Format_ARGB32);
200
applyTint(pClass, pClass.image);
205
KdmPixmap::loadSvg(PixmapStruct::PixmapClass &pClass)
207
if (pClass.svgRenderer)
209
if (pClass.fullpath.isEmpty())
211
pClass.svgRenderer = new QSvgRenderer(pClass.fullpath, this);
212
if (!pClass.svgRenderer->isValid()) {
213
delete pClass.svgRenderer;
214
pClass.svgRenderer = 0;
215
kWarning() << "failed to load " << pClass.fullpath ;
216
pClass.fullpath.clear();
219
if (pClass.svgRenderer->animated()) {
221
qsm = new QSignalMapper(this);
222
connect(qsm, SIGNAL(mapped(int)), SLOT(slotAnimate(int)));
224
qsm->setMapping(pClass.svgRenderer, state); // assuming we only load the current state
225
connect(pClass.svgRenderer, SIGNAL(repaintNeeded()), qsm, SLOT(map()));
227
pClass.svgSizeHint = pClass.svgElement.isEmpty() ?
228
pClass.svgRenderer->defaultSize() :
229
pClass.svgRenderer->boundsOnElement(pClass.svgElement).size().toSize();
234
KdmPixmap::sizeHint()
236
// use the pixmap size as the size hint
237
if (!pixmap.normal.svgImage) {
238
if (loadPixmap(pixmap.normal))
239
return pixmap.normal.image.size();
241
if (loadSvg(pixmap.normal))
242
return pixmap.normal.svgSizeHint;
244
return KdmItem::sizeHint();
248
KdmPixmap::setGeometry(QStack<QSize> &parentSizes, const QRect &newGeometry, bool force)
250
KdmItem::setGeometry(parentSizes, newGeometry, force);
251
pixmap.active.targetArea = QRect();
252
pixmap.prelight.targetArea = QRect();
253
pixmap.normal.targetArea = QRect();
257
KdmPixmap::calcTargetArea(PixmapStruct::PixmapClass &pClass, const QSize &sh)
260
sz.scale(area.size(), pClass.aspectMode);
261
pClass.targetArea.setSize(sz);
262
pClass.targetArea.moveCenter(area.center());
263
return pClass.targetArea.size() != pClass.readyPixmap.size();
267
KdmPixmap::drawContents(QPainter *p, const QRect &r)
269
PixmapStruct::PixmapClass &pClass = getCurClass();
271
if (pClass.targetArea.isEmpty()) {
274
if (pClass.svgImage) {
275
if (loadSvg(pClass)) {
276
if (!calcTargetArea(pClass, pClass.svgSizeHint))
278
scaledImage = QImage(pClass.targetArea.size(), QImage::Format_ARGB32);
280
QPainter pa(&scaledImage);
281
if (pClass.svgElement.isEmpty())
282
pClass.svgRenderer->render(&pa);
284
pClass.svgRenderer->render(&pa, pClass.svgElement);
285
applyTint(pClass, scaledImage);
288
if (loadPixmap(pClass)) {
289
if (!calcTargetArea(pClass, pClass.image.size()))
292
(area.size() != pClass.image.size()) ?
293
pClass.image.scaled(pClass.targetArea.size(),
294
Qt::IgnoreAspectRatio, Qt::SmoothTransformation) :
299
if (scaledImage.isNull()) {
300
p->fillRect(r, Qt::black);
304
pClass.readyPixmap = QPixmap::fromImage(scaledImage);
307
QRect tr = r.intersected(pClass.targetArea);
308
p->drawPixmap(tr.topLeft(), pClass.readyPixmap,
309
QRect(tr.topLeft() - pClass.targetArea.topLeft(), tr.size()));
313
KdmPixmap::applyTint(PixmapStruct::PixmapClass &pClass, QImage &img)
315
if (pClass.tint.rgba() == 0xFFFFFFFF)
319
int h = img.height();
320
int tint_red = pClass.tint.red();
321
int tint_green = pClass.tint.green();
322
int tint_blue = pClass.tint.blue();
323
int tint_alpha = pClass.tint.alpha();
325
for (int y = 0; y < h; ++y) {
326
QRgb *ls = (QRgb *)img.scanLine(y);
327
for (int x = 0; x < w; ++x) {
329
int r = qRed(l) * tint_red / 255;
330
int g = qGreen(l) * tint_green / 255;
331
int b = qBlue(l) * tint_blue / 255;
332
int a = qAlpha(l) * tint_alpha / 255;
333
ls[x] = qRgba(r, g, b, a);
338
KdmPixmap::PixmapStruct::PixmapClass &
339
KdmPixmap::getClass(ItemState sts)
342
(sts == Sprelight && pixmap.prelight.present) ?
344
(sts == Sactive && pixmap.active.present) ?
350
KdmPixmap::slotAnimate(int sts)
352
PixmapStruct::PixmapClass &pClass = getClass(ItemState(sts));
353
pClass.readyPixmap = QPixmap();
354
if (&pClass == &getCurClass())
359
KdmPixmap::statusChanged(bool descend)
361
KdmItem::statusChanged(descend);
362
if (!pixmap.active.present && !pixmap.prelight.present)
364
if ((state == Sprelight && !pixmap.prelight.present) ||
365
(state == Sactive && !pixmap.active.present))
370
#include "kdmpixmap.moc"