]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kpixmapmodifier.cpp
3bf11483b836bb05cc0e3810aeaee9902dcdfa4f
[dolphin.git] / src / kitemviews / private / kpixmapmodifier.cpp
1 // krazy:excludeall=copyright (email of Maxim is missing)
2 /*
3 This file is a part of the KDE project
4
5 Copyright © 2006 Zack Rusin <zack@kde.org>
6 Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
7
8 The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
9
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)
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
15 are met:
16
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.
22
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.
33 */
34
35 #include "kpixmapmodifier.h"
36
37 #include <QImage>
38 #include <QPainter>
39 #include <QGuiApplication>
40
41 static const quint32 stackBlur8Mul[255] =
42 {
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
59 };
60
61 static const quint32 stackBlur8Shr[255] =
62 {
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
79 };
80
81 static void blurHorizontal(QImage& image, unsigned int* stack, int div, int radius)
82 {
83 int stackindex;
84 int stackstart;
85
86 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
87 quint32 pixel;
88
89 int w = image.width();
90 int h = image.height();
91 int wm = w - 1;
92
93 unsigned int mulSum = stackBlur8Mul[radius];
94 unsigned int shrSum = stackBlur8Shr[radius];
95
96 unsigned int sum, sumIn, sumOut;
97
98 for (int y = 0; y < h; y++) {
99 sum = 0;
100 sumIn = 0;
101 sumOut = 0;
102
103 const int yw = y * w;
104 pixel = pixels[yw];
105 for (int i = 0; i <= radius; i++) {
106 stack[i] = qAlpha(pixel);
107
108 sum += stack[i] * (i + 1);
109 sumOut += stack[i];
110 }
111
112 for (int i = 1; i <= radius; i++) {
113 pixel = pixels[yw + qMin(i, wm)];
114
115 unsigned int* stackpix = &stack[i + radius];
116 *stackpix = qAlpha(pixel);
117
118 sum += *stackpix * (radius + 1 - i);
119 sumIn += *stackpix;
120 }
121
122 stackindex = radius;
123 for (int x = 0, i = yw; x < w; x++) {
124 pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
125
126 sum -= sumOut;
127
128 stackstart = stackindex + div - radius;
129 if (stackstart >= div) {
130 stackstart -= div;
131 }
132
133 unsigned int* stackpix = &stack[stackstart];
134
135 sumOut -= *stackpix;
136
137 pixel = pixels[yw + qMin(x + radius + 1, wm)];
138
139 *stackpix = qAlpha(pixel);
140
141 sumIn += *stackpix;
142 sum += sumIn;
143
144 if (++stackindex >= div) {
145 stackindex = 0;
146 }
147
148 stackpix = &stack[stackindex];
149
150 sumOut += *stackpix;
151 sumIn -= *stackpix;
152 }
153 }
154 }
155
156 static void blurVertical(QImage& image, unsigned int* stack, int div, int radius)
157 {
158 int stackindex;
159 int stackstart;
160
161 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
162 quint32 pixel;
163
164 int w = image.width();
165 int h = image.height();
166 int hm = h - 1;
167
168 int mul_sum = stackBlur8Mul[radius];
169 int shr_sum = stackBlur8Shr[radius];
170
171 unsigned int sum, sumIn, sumOut;
172
173 for (int x = 0; x < w; x++) {
174 sum = 0;
175 sumIn = 0;
176 sumOut = 0;
177
178 pixel = pixels[x];
179 for (int i = 0; i <= radius; i++) {
180 stack[i] = qAlpha(pixel);
181
182 sum += stack[i] * (i + 1);
183 sumOut += stack[i];
184 }
185
186 for (int i = 1; i <= radius; i++) {
187 pixel = pixels[qMin(i, hm) * w + x];
188
189 unsigned int* stackpix = &stack[i + radius];
190 *stackpix = qAlpha(pixel);
191
192 sum += *stackpix * (radius + 1 - i);
193 sumIn += *stackpix;
194 }
195
196 stackindex = radius;
197 for (int y = 0, i = x; y < h; y++, i += w) {
198 pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
199
200 sum -= sumOut;
201
202 stackstart = stackindex + div - radius;
203 if (stackstart >= div)
204 stackstart -= div;
205
206 unsigned int* stackpix = &stack[stackstart];
207
208 sumOut -= *stackpix;
209
210 pixel = pixels[qMin(y + radius + 1, hm) * w + x];
211
212 *stackpix = qAlpha(pixel);
213
214 sumIn += *stackpix;
215 sum += sumIn;
216
217 if (++stackindex >= div) {
218 stackindex = 0;
219 }
220
221 stackpix = &stack[stackindex];
222
223 sumOut += *stackpix;
224 sumIn -= *stackpix;
225 }
226 }
227 }
228
229 static void stackBlur(QImage& image, float radius)
230 {
231 radius = qRound(radius);
232
233 int div = int(radius * 2) + 1;
234 unsigned int* stack = new unsigned int[div];
235
236 blurHorizontal(image, stack, div, radius);
237 blurVertical(image, stack, div, radius);
238
239 delete [] stack;
240 }
241
242 static void shadowBlur(QImage& image, float radius, const QColor& color)
243 {
244 if (radius < 0) {
245 return;
246 }
247
248 if (radius > 0) {
249 stackBlur(image, radius);
250 }
251
252 // Correct the color and opacity of the shadow
253 QPainter p(&image);
254 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
255 p.fillRect(image.rect(), color);
256 }
257
258 namespace {
259 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
260 class TileSet
261 {
262 public:
263 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
264
265 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
266 RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
267 NumTiles };
268
269 TileSet()
270 {
271 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
272
273 QPainter p(&image);
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);
277 p.end();
278
279 shadowBlur(image, 3, Qt::black);
280
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);
290 }
291
292 void paint(QPainter* p, const QRect& r)
293 {
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]);
297 }
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]);
302 }
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]);
306 }
307 p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
308
309 const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
310 -(RightMargin + 1), -(BottomMargin + 1));
311 p->fillRect(contentRect, Qt::transparent);
312 }
313
314 QPixmap m_tiles[NumTiles];
315 };
316 }
317
318 void KPixmapModifier::scale(QPixmap& pixmap, const QSize& scaledSize)
319 {
320 if (scaledSize.isEmpty()) {
321 pixmap = QPixmap();
322 return;
323 }
324 qreal dpr = pixmap.devicePixelRatio();
325 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
326 pixmap.setDevicePixelRatio(dpr);
327 }
328
329 void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
330 {
331 static TileSet tileSet;
332 qreal dpr = qApp->devicePixelRatio();
333
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);
339
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);
344
345 QPainter painter;
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);
351
352 icon = framedIcon;
353 }
354
355 QSize KPixmapModifier::sizeInsideFrame(const QSize& frameSize)
356 {
357 return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
358 frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
359 }
360