1 // krazy:excludeall=copyright (email of Maxim is missing)
3 This file is a part of the KDE project
5 Copyright © 2006 Zack Rusin <zack@kde.org>
6 Copyright © 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 Copyright © 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
23 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include "kpixmapmodifier.h"
39 #include <QGuiApplication>
41 static const quint32 stackBlur8Mul
[255] =
43 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
44 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
45 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
46 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
47 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
48 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
49 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
50 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
51 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
52 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
53 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
54 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
55 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
56 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
57 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
58 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
61 static const quint32 stackBlur8Shr
[255] =
63 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
64 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
65 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
66 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
67 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
68 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
69 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
70 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
71 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
72 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
73 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
74 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
75 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
76 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
77 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
78 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
81 static void blurHorizontal(QImage
& image
, unsigned int* stack
, int div
, int radius
)
86 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
89 int w
= image
.width();
90 int h
= image
.height();
93 unsigned int mulSum
= stackBlur8Mul
[radius
];
94 unsigned int shrSum
= stackBlur8Shr
[radius
];
96 unsigned int sum
, sumIn
, sumOut
;
98 for (int y
= 0; y
< h
; y
++) {
103 const int yw
= y
* w
;
105 for (int i
= 0; i
<= radius
; i
++) {
106 stack
[i
] = qAlpha(pixel
);
108 sum
+= stack
[i
] * (i
+ 1);
112 for (int i
= 1; i
<= radius
; i
++) {
113 pixel
= pixels
[yw
+ qMin(i
, wm
)];
115 unsigned int* stackpix
= &stack
[i
+ radius
];
116 *stackpix
= qAlpha(pixel
);
118 sum
+= *stackpix
* (radius
+ 1 - i
);
123 for (int x
= 0, i
= yw
; x
< w
; x
++) {
124 pixels
[i
++] = (((sum
* mulSum
) >> shrSum
) << 24) & 0xff000000;
128 stackstart
= stackindex
+ div
- radius
;
129 if (stackstart
>= div
) {
133 unsigned int* stackpix
= &stack
[stackstart
];
137 pixel
= pixels
[yw
+ qMin(x
+ radius
+ 1, wm
)];
139 *stackpix
= qAlpha(pixel
);
144 if (++stackindex
>= div
) {
148 stackpix
= &stack
[stackindex
];
156 static void blurVertical(QImage
& image
, unsigned int* stack
, int div
, int radius
)
161 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
164 int w
= image
.width();
165 int h
= image
.height();
168 int mul_sum
= stackBlur8Mul
[radius
];
169 int shr_sum
= stackBlur8Shr
[radius
];
171 unsigned int sum
, sumIn
, sumOut
;
173 for (int x
= 0; x
< w
; x
++) {
179 for (int i
= 0; i
<= radius
; i
++) {
180 stack
[i
] = qAlpha(pixel
);
182 sum
+= stack
[i
] * (i
+ 1);
186 for (int i
= 1; i
<= radius
; i
++) {
187 pixel
= pixels
[qMin(i
, hm
) * w
+ x
];
189 unsigned int* stackpix
= &stack
[i
+ radius
];
190 *stackpix
= qAlpha(pixel
);
192 sum
+= *stackpix
* (radius
+ 1 - i
);
197 for (int y
= 0, i
= x
; y
< h
; y
++, i
+= w
) {
198 pixels
[i
] = (((sum
* mul_sum
) >> shr_sum
) << 24) & 0xff000000;
202 stackstart
= stackindex
+ div
- radius
;
203 if (stackstart
>= div
)
206 unsigned int* stackpix
= &stack
[stackstart
];
210 pixel
= pixels
[qMin(y
+ radius
+ 1, hm
) * w
+ x
];
212 *stackpix
= qAlpha(pixel
);
217 if (++stackindex
>= div
) {
221 stackpix
= &stack
[stackindex
];
229 static void stackBlur(QImage
& image
, float radius
)
231 radius
= qRound(radius
);
233 int div
= int(radius
* 2) + 1;
234 unsigned int* stack
= new unsigned int[div
];
236 blurHorizontal(image
, stack
, div
, radius
);
237 blurVertical(image
, stack
, div
, radius
);
242 static void shadowBlur(QImage
& image
, float radius
, const QColor
& color
)
249 stackBlur(image
, radius
);
252 // Correct the color and opacity of the shadow
254 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
255 p
.fillRect(image
.rect(), color
);
259 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
263 enum { LeftMargin
= 3, TopMargin
= 2, RightMargin
= 3, BottomMargin
= 4 };
265 enum Tile
{ TopLeftCorner
= 0, TopSide
, TopRightCorner
, LeftSide
,
266 RightSide
, BottomLeftCorner
, BottomSide
, BottomRightCorner
,
271 QImage
image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied
);
274 p
.setCompositionMode(QPainter::CompositionMode_Source
);
275 p
.fillRect(image
.rect(), Qt::transparent
);
276 p
.fillRect(image
.rect().adjusted(3, 3, -3, -3), Qt::black
);
279 shadowBlur(image
, 3, Qt::black
);
281 QPixmap pixmap
= QPixmap::fromImage(image
);
282 m_tiles
[TopLeftCorner
] = pixmap
.copy(0, 0, 8, 8);
283 m_tiles
[TopSide
] = pixmap
.copy(8, 0, 8, 8);
284 m_tiles
[TopRightCorner
] = pixmap
.copy(16, 0, 8, 8);
285 m_tiles
[LeftSide
] = pixmap
.copy(0, 8, 8, 8);
286 m_tiles
[RightSide
] = pixmap
.copy(16, 8, 8, 8);
287 m_tiles
[BottomLeftCorner
] = pixmap
.copy(0, 16, 8, 8);
288 m_tiles
[BottomSide
] = pixmap
.copy(8, 16, 8, 8);
289 m_tiles
[BottomRightCorner
] = pixmap
.copy(16, 16, 8, 8);
292 void paint(QPainter
* p
, const QRect
& r
)
294 p
->drawPixmap(r
.topLeft(), m_tiles
[TopLeftCorner
]);
295 if (r
.width() - 16 > 0) {
296 p
->drawTiledPixmap(r
.x() + 8, r
.y(), r
.width() - 16, 8, m_tiles
[TopSide
]);
298 p
->drawPixmap(r
.right() - 8 + 1, r
.y(), m_tiles
[TopRightCorner
]);
299 if (r
.height() - 16 > 0) {
300 p
->drawTiledPixmap(r
.x(), r
.y() + 8, 8, r
.height() - 16, m_tiles
[LeftSide
]);
301 p
->drawTiledPixmap(r
.right() - 8 + 1, r
.y() + 8, 8, r
.height() - 16, m_tiles
[RightSide
]);
303 p
->drawPixmap(r
.x(), r
.bottom() - 8 + 1, m_tiles
[BottomLeftCorner
]);
304 if (r
.width() - 16 > 0) {
305 p
->drawTiledPixmap(r
.x() + 8, r
.bottom() - 8 + 1, r
.width() - 16, 8, m_tiles
[BottomSide
]);
307 p
->drawPixmap(r
.right() - 8 + 1, r
.bottom() - 8 + 1, m_tiles
[BottomRightCorner
]);
309 const QRect contentRect
= r
.adjusted(LeftMargin
+ 1, TopMargin
+ 1,
310 -(RightMargin
+ 1), -(BottomMargin
+ 1));
311 p
->fillRect(contentRect
, Qt::transparent
);
314 QPixmap m_tiles
[NumTiles
];
318 void KPixmapModifier::scale(QPixmap
& pixmap
, const QSize
& scaledSize
)
320 if (scaledSize
.isEmpty()) {
324 qreal dpr
= pixmap
.devicePixelRatio();
325 pixmap
= pixmap
.scaled(scaledSize
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
326 pixmap
.setDevicePixelRatio(dpr
);
329 void KPixmapModifier::applyFrame(QPixmap
& icon
, const QSize
& scaledSize
)
331 static TileSet tileSet
;
332 qreal dpr
= qApp
->devicePixelRatio();
334 // Resize the icon to the maximum size minus the space required for the frame
335 const QSize
size(scaledSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
,
336 scaledSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);
337 scale(icon
, size
* dpr
);
338 icon
.setDevicePixelRatio(dpr
);
340 QPixmap
framedIcon(icon
.size().width() + (TileSet::LeftMargin
+ TileSet::RightMargin
) * dpr
,
341 icon
.size().height() + (TileSet::TopMargin
+ TileSet::BottomMargin
) * dpr
);
342 framedIcon
.setDevicePixelRatio(dpr
);
343 framedIcon
.fill(Qt::transparent
);
346 painter
.begin(&framedIcon
);
347 painter
.setCompositionMode(QPainter::CompositionMode_Source
);
348 tileSet
.paint(&painter
, QRect(QPoint(0,0), framedIcon
.size() / dpr
));
349 painter
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
350 painter
.drawPixmap(TileSet::LeftMargin
, TileSet::TopMargin
, icon
);
355 QSize
KPixmapModifier::sizeInsideFrame(const QSize
& frameSize
)
357 return QSize(frameSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
,
358 frameSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);