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"
41 #include <QGuiApplication>
43 static const quint32 stackBlur8Mul
[255] =
45 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
46 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
47 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
48 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
49 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
50 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
51 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
52 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
53 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
54 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
55 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
56 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
57 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
58 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
59 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
60 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
63 static const quint32 stackBlur8Shr
[255] =
65 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
66 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
67 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
68 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
69 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
70 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
71 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
72 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
73 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
74 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
75 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
76 23, 23, 23, 23, 23, 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, 24,
79 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
80 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
83 static void blurHorizontal(QImage
& image
, unsigned int* stack
, int div
, int radius
)
88 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
91 int w
= image
.width();
92 int h
= image
.height();
95 unsigned int mulSum
= stackBlur8Mul
[radius
];
96 unsigned int shrSum
= stackBlur8Shr
[radius
];
98 unsigned int sum
, sumIn
, sumOut
;
100 for (int y
= 0; y
< h
; y
++) {
105 const int yw
= y
* w
;
107 for (int i
= 0; i
<= radius
; i
++) {
108 stack
[i
] = qAlpha(pixel
);
110 sum
+= stack
[i
] * (i
+ 1);
114 for (int i
= 1; i
<= radius
; i
++) {
115 pixel
= pixels
[yw
+ qMin(i
, wm
)];
117 unsigned int* stackpix
= &stack
[i
+ radius
];
118 *stackpix
= qAlpha(pixel
);
120 sum
+= *stackpix
* (radius
+ 1 - i
);
125 for (int x
= 0, i
= yw
; x
< w
; x
++) {
126 pixels
[i
++] = (((sum
* mulSum
) >> shrSum
) << 24) & 0xff000000;
130 stackstart
= stackindex
+ div
- radius
;
131 if (stackstart
>= div
) {
135 unsigned int* stackpix
= &stack
[stackstart
];
139 pixel
= pixels
[yw
+ qMin(x
+ radius
+ 1, wm
)];
141 *stackpix
= qAlpha(pixel
);
146 if (++stackindex
>= div
) {
150 stackpix
= &stack
[stackindex
];
158 static void blurVertical(QImage
& image
, unsigned int* stack
, int div
, int radius
)
163 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
166 int w
= image
.width();
167 int h
= image
.height();
170 int mul_sum
= stackBlur8Mul
[radius
];
171 int shr_sum
= stackBlur8Shr
[radius
];
173 unsigned int sum
, sumIn
, sumOut
;
175 for (int x
= 0; x
< w
; x
++) {
181 for (int i
= 0; i
<= radius
; i
++) {
182 stack
[i
] = qAlpha(pixel
);
184 sum
+= stack
[i
] * (i
+ 1);
188 for (int i
= 1; i
<= radius
; i
++) {
189 pixel
= pixels
[qMin(i
, hm
) * w
+ x
];
191 unsigned int* stackpix
= &stack
[i
+ radius
];
192 *stackpix
= qAlpha(pixel
);
194 sum
+= *stackpix
* (radius
+ 1 - i
);
199 for (int y
= 0, i
= x
; y
< h
; y
++, i
+= w
) {
200 pixels
[i
] = (((sum
* mul_sum
) >> shr_sum
) << 24) & 0xff000000;
204 stackstart
= stackindex
+ div
- radius
;
205 if (stackstart
>= div
)
208 unsigned int* stackpix
= &stack
[stackstart
];
212 pixel
= pixels
[qMin(y
+ radius
+ 1, hm
) * w
+ x
];
214 *stackpix
= qAlpha(pixel
);
219 if (++stackindex
>= div
) {
223 stackpix
= &stack
[stackindex
];
231 static void stackBlur(QImage
& image
, float radius
)
233 radius
= qRound(radius
);
235 int div
= int(radius
* 2) + 1;
236 unsigned int* stack
= new unsigned int[div
];
238 blurHorizontal(image
, stack
, div
, radius
);
239 blurVertical(image
, stack
, div
, radius
);
244 static void shadowBlur(QImage
& image
, float radius
, const QColor
& color
)
251 stackBlur(image
, radius
);
254 // Correct the color and opacity of the shadow
256 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
257 p
.fillRect(image
.rect(), color
);
261 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
265 enum { LeftMargin
= 3, TopMargin
= 2, RightMargin
= 3, BottomMargin
= 4 };
267 enum Tile
{ TopLeftCorner
= 0, TopSide
, TopRightCorner
, LeftSide
,
268 RightSide
, BottomLeftCorner
, BottomSide
, BottomRightCorner
,
273 QImage
image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied
);
276 p
.setCompositionMode(QPainter::CompositionMode_Source
);
277 p
.fillRect(image
.rect(), Qt::transparent
);
278 p
.fillRect(image
.rect().adjusted(3, 3, -3, -3), Qt::black
);
281 shadowBlur(image
, 3, Qt::black
);
283 QPixmap pixmap
= QPixmap::fromImage(image
);
284 m_tiles
[TopLeftCorner
] = pixmap
.copy(0, 0, 8, 8);
285 m_tiles
[TopSide
] = pixmap
.copy(8, 0, 8, 8);
286 m_tiles
[TopRightCorner
] = pixmap
.copy(16, 0, 8, 8);
287 m_tiles
[LeftSide
] = pixmap
.copy(0, 8, 8, 8);
288 m_tiles
[RightSide
] = pixmap
.copy(16, 8, 8, 8);
289 m_tiles
[BottomLeftCorner
] = pixmap
.copy(0, 16, 8, 8);
290 m_tiles
[BottomSide
] = pixmap
.copy(8, 16, 8, 8);
291 m_tiles
[BottomRightCorner
] = pixmap
.copy(16, 16, 8, 8);
294 void paint(QPainter
* p
, const QRect
& r
)
296 p
->drawPixmap(r
.topLeft(), m_tiles
[TopLeftCorner
]);
297 if (r
.width() - 16 > 0) {
298 p
->drawTiledPixmap(r
.x() + 8, r
.y(), r
.width() - 16, 8, m_tiles
[TopSide
]);
300 p
->drawPixmap(r
.right() - 8 + 1, r
.y(), m_tiles
[TopRightCorner
]);
301 if (r
.height() - 16 > 0) {
302 p
->drawTiledPixmap(r
.x(), r
.y() + 8, 8, r
.height() - 16, m_tiles
[LeftSide
]);
303 p
->drawTiledPixmap(r
.right() - 8 + 1, r
.y() + 8, 8, r
.height() - 16, m_tiles
[RightSide
]);
305 p
->drawPixmap(r
.x(), r
.bottom() - 8 + 1, m_tiles
[BottomLeftCorner
]);
306 if (r
.width() - 16 > 0) {
307 p
->drawTiledPixmap(r
.x() + 8, r
.bottom() - 8 + 1, r
.width() - 16, 8, m_tiles
[BottomSide
]);
309 p
->drawPixmap(r
.right() - 8 + 1, r
.bottom() - 8 + 1, m_tiles
[BottomRightCorner
]);
311 const QRect contentRect
= r
.adjusted(LeftMargin
+ 1, TopMargin
+ 1,
312 -(RightMargin
+ 1), -(BottomMargin
+ 1));
313 p
->fillRect(contentRect
, Qt::transparent
);
316 QPixmap m_tiles
[NumTiles
];
320 void KPixmapModifier::scale(QPixmap
& pixmap
, const QSize
& scaledSize
)
322 if (scaledSize
.isEmpty()) {
326 qreal dpr
= pixmap
.devicePixelRatio();
327 pixmap
= pixmap
.scaled(scaledSize
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
328 pixmap
.setDevicePixelRatio(dpr
);
331 void KPixmapModifier::applyFrame(QPixmap
& icon
, const QSize
& scaledSize
)
333 static TileSet tileSet
;
334 qreal dpr
= qApp
->devicePixelRatio();
336 // Resize the icon to the maximum size minus the space required for the frame
337 const QSize
size(scaledSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
,
338 scaledSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);
339 scale(icon
, size
* dpr
);
340 icon
.setDevicePixelRatio(dpr
);
342 QPixmap
framedIcon(icon
.size().width() + (TileSet::LeftMargin
+ TileSet::RightMargin
) * dpr
,
343 icon
.size().height() + (TileSet::TopMargin
+ TileSet::BottomMargin
) * dpr
);
344 framedIcon
.setDevicePixelRatio(dpr
);
345 framedIcon
.fill(Qt::transparent
);
348 painter
.begin(&framedIcon
);
349 painter
.setCompositionMode(QPainter::CompositionMode_Source
);
350 tileSet
.paint(&painter
, QRect(QPoint(0,0), framedIcon
.size() / dpr
));
351 painter
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
352 painter
.drawPixmap(TileSet::LeftMargin
, TileSet::TopMargin
, icon
);
357 QSize
KPixmapModifier::sizeInsideFrame(const QSize
& frameSize
)
359 return QSize(frameSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
,
360 frameSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);