File indexing completed on 2024-04-28 16:55:26

0001 /*
0002  * shadowengine.cpp - Emerald window decoration for KDE
0003  *
0004  * Copyright (c) 2010 Christoph Feck <christoph@maxiom.de>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program; if not, write to the Free Software
0018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0019  *
0020  */
0021 
0022 #include "shadowengine.h"
0023 
0024 #include <QImage>
0025 
0026 #include <cmath>
0027 
0028 namespace Smaragd
0029 {
0030 
0031 QImage createShadowImage(const ShadowSettings &settings)
0032 {
0033     const int radius = settings.radius + 32;
0034     const int size = 2 * radius + 1;
0035 
0036     QImage shadowImage(size, size, QImage::Format_ARGB32_Premultiplied);
0037     shadowImage.fill(0);
0038 
0039     QRadialGradient gradient(radius + 0.5, radius + 0.5, radius + 0.5);
0040     for (qreal v = 0.0; v <= 1.0; v += 1.0 / 64) {
0041         qreal k;
0042         QColor color = settings.color;
0043         if (v * radius < radius - (32.0 + settings.size)) {
0044             k = 0.0;
0045         } else {
0046             qreal x = (radius - 32.0) / radius;
0047             k = (v - x) / (1.0 - x);
0048             k = pow(1.0 - k, settings.linearDecay) * exp(-settings.exponentialDecay * k);
0049         }
0050         color.setAlpha(qBound(0, qRound(color.alpha() * k), 255));
0051         gradient.setColorAt(v, color);
0052     }
0053 
0054     QPainter p(&shadowImage);
0055     p.setCompositionMode(QPainter::CompositionMode_Source);
0056     p.setRenderHint(QPainter::Antialiasing, true);
0057     p.setBrush(gradient);
0058     p.setPen(Qt::NoPen);
0059     p.drawEllipse(0, 0, size, size);
0060     p.end();
0061 
0062     return shadowImage;
0063 }
0064 
0065 void paintShadow(QPainter *p, const QRect &r, const ShadowSettings &settings, const QImage &shadowImage)
0066 {
0067     const int s = -settings.size;
0068     QRect rect = r.adjusted(s - 32, s - 32, 32 - s, 32 - s);
0069     rect.adjust(settings.offsetX, settings.offsetY, settings.offsetX, settings.offsetY);
0070     int radius = shadowImage.width() / 2;
0071 
0072     // corners
0073     for (int i = 0; i < 4; ++i) {
0074         const int x = i & 1 ? rect.x() : rect.x() + rect.width() - radius;
0075         const int y = i & 2 ? rect.y() : rect.y() + rect.height() - radius;
0076         p->drawImage(x, y, shadowImage,
0077             i & 1 ? 0 : radius + 1, i & 2 ? 0 : radius + 1, radius, radius);
0078     }
0079 
0080     // sides
0081     for (int i = 0; i < 2; ++i) {
0082         const int x = rect.x() + radius;
0083         const int y = i & 1 ? rect.y() : rect.y() + rect.height() - radius;
0084         p->drawTiledPixmap(x, y, rect.width() - 2 * radius, radius,
0085             QPixmap::fromImage(shadowImage.copy(radius, i & 1 ? 0 : radius + 1, 1, radius)));
0086     }
0087     for (int i = 0; i < 2; ++i) {
0088         const int x = i & 1 ? rect.x() : rect.x() + rect.width() - radius;
0089         const int y = rect.y() + radius;
0090         p->drawTiledPixmap(x, y, radius, rect.height() - 2 * radius,
0091             QPixmap::fromImage(shadowImage.copy(i & 1 ? 0 : radius + 1, radius, radius, 1)));
0092     }
0093 
0094     // center
0095     QRgb pixel = shadowImage.pixel(radius, radius);
0096     if (shadowImage.format() == QImage::Format_ARGB32_Premultiplied) {
0097         const int alpha = qAlpha(pixel);
0098         if (alpha != 0 && alpha != 255) {
0099             pixel = qRgba(qBound(0, qRed(pixel) * 255 / alpha, 255),
0100                 qBound(0, qGreen(pixel) * 255 / alpha, 255),
0101                 qBound(0, qBlue(pixel) * 255 / alpha, 255),
0102                 alpha);
0103         }
0104     }
0105     p->fillRect(rect.adjusted(radius, radius, -radius, -radius), QColor::fromRgba(pixel));
0106 }
0107 
0108 }; // namespace Smaragd
0109