]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kpixmapmodifier.cpp
Merge branch 'Applications/16.04'
[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 <QPixmap>
40 #include <QSize>
41 #include <QGuiApplication>
42
43 static const quint32 stackBlur8Mul[255] =
44 {
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
61 };
62
63 static const quint32 stackBlur8Shr[255] =
64 {
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
81 };
82
83 static void blurHorizontal(QImage& image, unsigned int* stack, int div, int radius)
84 {
85 int stackindex;
86 int stackstart;
87
88 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
89 quint32 pixel;
90
91 int w = image.width();
92 int h = image.height();
93 int wm = w - 1;
94
95 unsigned int mulSum = stackBlur8Mul[radius];
96 unsigned int shrSum = stackBlur8Shr[radius];
97
98 unsigned int sum, sumIn, sumOut;
99
100 for (int y = 0; y < h; y++) {
101 sum = 0;
102 sumIn = 0;
103 sumOut = 0;
104
105 const int yw = y * w;
106 pixel = pixels[yw];
107 for (int i = 0; i <= radius; i++) {
108 stack[i] = qAlpha(pixel);
109
110 sum += stack[i] * (i + 1);
111 sumOut += stack[i];
112 }
113
114 for (int i = 1; i <= radius; i++) {
115 pixel = pixels[yw + qMin(i, wm)];
116
117 unsigned int* stackpix = &stack[i + radius];
118 *stackpix = qAlpha(pixel);
119
120 sum += *stackpix * (radius + 1 - i);
121 sumIn += *stackpix;
122 }
123
124 stackindex = radius;
125 for (int x = 0, i = yw; x < w; x++) {
126 pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
127
128 sum -= sumOut;
129
130 stackstart = stackindex + div - radius;
131 if (stackstart >= div) {
132 stackstart -= div;
133 }
134
135 unsigned int* stackpix = &stack[stackstart];
136
137 sumOut -= *stackpix;
138
139 pixel = pixels[yw + qMin(x + radius + 1, wm)];
140
141 *stackpix = qAlpha(pixel);
142
143 sumIn += *stackpix;
144 sum += sumIn;
145
146 if (++stackindex >= div) {
147 stackindex = 0;
148 }
149
150 stackpix = &stack[stackindex];
151
152 sumOut += *stackpix;
153 sumIn -= *stackpix;
154 }
155 }
156 }
157
158 static void blurVertical(QImage& image, unsigned int* stack, int div, int radius)
159 {
160 int stackindex;
161 int stackstart;
162
163 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
164 quint32 pixel;
165
166 int w = image.width();
167 int h = image.height();
168 int hm = h - 1;
169
170 int mul_sum = stackBlur8Mul[radius];
171 int shr_sum = stackBlur8Shr[radius];
172
173 unsigned int sum, sumIn, sumOut;
174
175 for (int x = 0; x < w; x++) {
176 sum = 0;
177 sumIn = 0;
178 sumOut = 0;
179
180 pixel = pixels[x];
181 for (int i = 0; i <= radius; i++) {
182 stack[i] = qAlpha(pixel);
183
184 sum += stack[i] * (i + 1);
185 sumOut += stack[i];
186 }
187
188 for (int i = 1; i <= radius; i++) {
189 pixel = pixels[qMin(i, hm) * w + x];
190
191 unsigned int* stackpix = &stack[i + radius];
192 *stackpix = qAlpha(pixel);
193
194 sum += *stackpix * (radius + 1 - i);
195 sumIn += *stackpix;
196 }
197
198 stackindex = radius;
199 for (int y = 0, i = x; y < h; y++, i += w) {
200 pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
201
202 sum -= sumOut;
203
204 stackstart = stackindex + div - radius;
205 if (stackstart >= div)
206 stackstart -= div;
207
208 unsigned int* stackpix = &stack[stackstart];
209
210 sumOut -= *stackpix;
211
212 pixel = pixels[qMin(y + radius + 1, hm) * w + x];
213
214 *stackpix = qAlpha(pixel);
215
216 sumIn += *stackpix;
217 sum += sumIn;
218
219 if (++stackindex >= div) {
220 stackindex = 0;
221 }
222
223 stackpix = &stack[stackindex];
224
225 sumOut += *stackpix;
226 sumIn -= *stackpix;
227 }
228 }
229 }
230
231 static void stackBlur(QImage& image, float radius)
232 {
233 radius = qRound(radius);
234
235 int div = int(radius * 2) + 1;
236 unsigned int* stack = new unsigned int[div];
237
238 blurHorizontal(image, stack, div, radius);
239 blurVertical(image, stack, div, radius);
240
241 delete [] stack;
242 }
243
244 static void shadowBlur(QImage& image, float radius, const QColor& color)
245 {
246 if (radius < 0) {
247 return;
248 }
249
250 if (radius > 0) {
251 stackBlur(image, radius);
252 }
253
254 // Correct the color and opacity of the shadow
255 QPainter p(&image);
256 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
257 p.fillRect(image.rect(), color);
258 }
259
260 namespace {
261 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
262 class TileSet
263 {
264 public:
265 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
266
267 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
268 RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
269 NumTiles };
270
271 TileSet()
272 {
273 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
274
275 QPainter p(&image);
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);
279 p.end();
280
281 shadowBlur(image, 3, Qt::black);
282
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);
292 }
293
294 void paint(QPainter* p, const QRect& r)
295 {
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]);
299 }
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]);
304 }
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]);
308 }
309 p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
310
311 const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
312 -(RightMargin + 1), -(BottomMargin + 1));
313 p->fillRect(contentRect, Qt::transparent);
314 }
315
316 QPixmap m_tiles[NumTiles];
317 };
318 }
319
320 void KPixmapModifier::scale(QPixmap& pixmap, const QSize& scaledSize)
321 {
322 if (scaledSize.isEmpty()) {
323 pixmap = QPixmap();
324 return;
325 }
326 qreal dpr = pixmap.devicePixelRatio();
327 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
328 pixmap.setDevicePixelRatio(dpr);
329 }
330
331 void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
332 {
333 static TileSet tileSet;
334 qreal dpr = qApp->devicePixelRatio();
335
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);
341
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);
346
347 QPainter painter;
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);
353
354 icon = framedIcon;
355 }
356
357 QSize KPixmapModifier::sizeInsideFrame(const QSize& frameSize)
358 {
359 return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
360 frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
361 }
362