1 // krazy:excludeall=copyright (email of Maxim is missing)
3 This file is a part of the KDE project
5 SPDX-FileCopyrightText: 2006 Zack Rusin <zack@kde.org>
6 SPDX-FileCopyrightText: 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
8 The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
10 This implementation is based on the version in Anti-Grain Geometry Version 2.4,
11 SPDX-FileCopyrightText: 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
13 SPDX-License-Identifier: BSD-2-Clause
16 #include "kpixmapmodifier.h"
18 #include <QGuiApplication>
22 static const quint32 stackBlur8Mul
[255] = {
23 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312,
24 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312,
25 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278,
26 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312,
27 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399,
28 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278,
29 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408,
30 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312,
31 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259};
33 static const quint32 stackBlur8Shr
[255] = {
34 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
35 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
36 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
37 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
38 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
39 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
40 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24};
42 static void blurHorizontal(QImage
&image
, unsigned int *stack
, int div
, int radius
)
47 quint32
*const pixels
= reinterpret_cast<quint32
*>(image
.bits());
50 int w
= image
.width();
51 int h
= image
.height();
54 unsigned int mulSum
= stackBlur8Mul
[radius
];
55 unsigned int shrSum
= stackBlur8Shr
[radius
];
57 unsigned int sum
, sumIn
, sumOut
;
59 for (int y
= 0; y
< h
; y
++) {
66 for (int i
= 0; i
<= radius
; i
++) {
67 stack
[i
] = qAlpha(pixel
);
69 sum
+= stack
[i
] * (i
+ 1);
73 for (int i
= 1; i
<= radius
; i
++) {
74 pixel
= pixels
[yw
+ qMin(i
, wm
)];
76 unsigned int *stackpix
= &stack
[i
+ radius
];
77 *stackpix
= qAlpha(pixel
);
79 sum
+= *stackpix
* (radius
+ 1 - i
);
84 for (int x
= 0, i
= yw
; x
< w
; x
++) {
85 pixels
[i
++] = (((sum
* mulSum
) >> shrSum
) << 24) & 0xff000000;
89 stackstart
= stackindex
+ div
- radius
;
90 if (stackstart
>= div
) {
94 unsigned int *stackpix
= &stack
[stackstart
];
98 pixel
= pixels
[yw
+ qMin(x
+ radius
+ 1, wm
)];
100 *stackpix
= qAlpha(pixel
);
105 if (++stackindex
>= div
) {
109 stackpix
= &stack
[stackindex
];
117 static void blurVertical(QImage
&image
, unsigned int *stack
, int div
, int radius
)
122 quint32
*const pixels
= reinterpret_cast<quint32
*>(image
.bits());
125 int w
= image
.width();
126 int h
= image
.height();
129 int mul_sum
= stackBlur8Mul
[radius
];
130 int shr_sum
= stackBlur8Shr
[radius
];
132 unsigned int sum
, sumIn
, sumOut
;
134 for (int x
= 0; x
< w
; x
++) {
140 for (int i
= 0; i
<= radius
; i
++) {
141 stack
[i
] = qAlpha(pixel
);
143 sum
+= stack
[i
] * (i
+ 1);
147 for (int i
= 1; i
<= radius
; i
++) {
148 pixel
= pixels
[qMin(i
, hm
) * w
+ x
];
150 unsigned int *stackpix
= &stack
[i
+ radius
];
151 *stackpix
= qAlpha(pixel
);
153 sum
+= *stackpix
* (radius
+ 1 - i
);
158 for (int y
= 0, i
= x
; y
< h
; y
++, i
+= w
) {
159 pixels
[i
] = (((sum
* mul_sum
) >> shr_sum
) << 24) & 0xff000000;
163 stackstart
= stackindex
+ div
- radius
;
164 if (stackstart
>= div
)
167 unsigned int *stackpix
= &stack
[stackstart
];
171 pixel
= pixels
[qMin(y
+ radius
+ 1, hm
) * w
+ x
];
173 *stackpix
= qAlpha(pixel
);
178 if (++stackindex
>= div
) {
182 stackpix
= &stack
[stackindex
];
190 static void stackBlur(QImage
&image
, float radius
)
192 radius
= qRound(radius
);
194 int div
= int(radius
* 2) + 1;
195 unsigned int *stack
= new unsigned int[div
];
197 blurHorizontal(image
, stack
, div
, radius
);
198 blurVertical(image
, stack
, div
, radius
);
203 static void shadowBlur(QImage
&image
, float radius
, const QColor
&color
)
210 stackBlur(image
, radius
);
213 // Correct the color and opacity of the shadow
215 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
216 p
.fillRect(image
.rect(), color
);
221 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
225 enum { LeftMargin
= 3, TopMargin
= 2, RightMargin
= 3, BottomMargin
= 4 };
227 enum Tile
{ TopLeftCorner
= 0, TopSide
, TopRightCorner
, LeftSide
, RightSide
, BottomLeftCorner
, BottomSide
, BottomRightCorner
, NumTiles
};
231 QImage
image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied
);
234 p
.setCompositionMode(QPainter::CompositionMode_Source
);
235 p
.fillRect(image
.rect(), Qt::transparent
);
236 p
.fillRect(image
.rect().adjusted(3, 3, -3, -3), Qt::black
);
239 shadowBlur(image
, 3, Qt::black
);
241 QPixmap pixmap
= QPixmap::fromImage(image
);
242 m_tiles
[TopLeftCorner
] = pixmap
.copy(0, 0, 8, 8);
243 m_tiles
[TopSide
] = pixmap
.copy(8, 0, 8, 8);
244 m_tiles
[TopRightCorner
] = pixmap
.copy(16, 0, 8, 8);
245 m_tiles
[LeftSide
] = pixmap
.copy(0, 8, 8, 8);
246 m_tiles
[RightSide
] = pixmap
.copy(16, 8, 8, 8);
247 m_tiles
[BottomLeftCorner
] = pixmap
.copy(0, 16, 8, 8);
248 m_tiles
[BottomSide
] = pixmap
.copy(8, 16, 8, 8);
249 m_tiles
[BottomRightCorner
] = pixmap
.copy(16, 16, 8, 8);
252 void paint(QPainter
*p
, const QRect
&r
)
254 p
->drawPixmap(r
.topLeft(), m_tiles
[TopLeftCorner
]);
255 if (r
.width() - 16 > 0) {
256 p
->drawTiledPixmap(r
.x() + 8, r
.y(), r
.width() - 16, 8, m_tiles
[TopSide
]);
258 p
->drawPixmap(r
.right() - 8 + 1, r
.y(), m_tiles
[TopRightCorner
]);
259 if (r
.height() - 16 > 0) {
260 p
->drawTiledPixmap(r
.x(), r
.y() + 8, 8, r
.height() - 16, m_tiles
[LeftSide
]);
261 p
->drawTiledPixmap(r
.right() - 8 + 1, r
.y() + 8, 8, r
.height() - 16, m_tiles
[RightSide
]);
263 p
->drawPixmap(r
.x(), r
.bottom() - 8 + 1, m_tiles
[BottomLeftCorner
]);
264 if (r
.width() - 16 > 0) {
265 p
->drawTiledPixmap(r
.x() + 8, r
.bottom() - 8 + 1, r
.width() - 16, 8, m_tiles
[BottomSide
]);
267 p
->drawPixmap(r
.right() - 8 + 1, r
.bottom() - 8 + 1, m_tiles
[BottomRightCorner
]);
269 const QRect contentRect
= r
.adjusted(LeftMargin
+ 1, TopMargin
+ 1, -(RightMargin
+ 1), -(BottomMargin
+ 1));
270 p
->fillRect(contentRect
, Qt::transparent
);
273 QPixmap m_tiles
[NumTiles
];
277 void KPixmapModifier::scale(QPixmap
&pixmap
, const QSize
&scaledSize
)
279 if (scaledSize
.isEmpty() || pixmap
.isNull()) {
283 qreal dpr
= pixmap
.devicePixelRatio();
284 pixmap
= pixmap
.scaled(scaledSize
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
285 pixmap
.setDevicePixelRatio(dpr
);
288 void KPixmapModifier::applyFrame(QPixmap
&icon
, const QSize
&scaledSize
)
291 icon
= QPixmap(scaledSize
);
292 icon
.fill(Qt::transparent
);
296 static TileSet tileSet
;
297 qreal dpr
= qApp
->devicePixelRatio();
299 // Resize the icon to the maximum size minus the space required for the frame
300 const QSize
size(scaledSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
, scaledSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);
301 scale(icon
, size
* dpr
);
302 icon
.setDevicePixelRatio(dpr
);
304 QPixmap
framedIcon(icon
.size().width() + (TileSet::LeftMargin
+ TileSet::RightMargin
) * dpr
,
305 icon
.size().height() + (TileSet::TopMargin
+ TileSet::BottomMargin
) * dpr
);
306 framedIcon
.setDevicePixelRatio(dpr
);
307 framedIcon
.fill(Qt::transparent
);
310 painter
.begin(&framedIcon
);
311 painter
.setCompositionMode(QPainter::CompositionMode_Source
);
312 tileSet
.paint(&painter
, QRect(QPoint(0, 0), framedIcon
.size() / dpr
));
313 painter
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
314 painter
.drawPixmap(TileSet::LeftMargin
, TileSet::TopMargin
, icon
);
319 QSize
KPixmapModifier::sizeInsideFrame(const QSize
&frameSize
)
321 return QSize(frameSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
, frameSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);