]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kpixmapmodifier.cpp
GIT_SILENT Sync po/docbooks with svn
[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 SPDX-FileCopyrightText: 2006 Zack Rusin <zack@kde.org>
6 SPDX-FileCopyrightText: 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 SPDX-FileCopyrightText: 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
12
13 SPDX-License-Identifier: BSD-2-Clause
14 */
15
16 #include "kpixmapmodifier.h"
17
18 #include <QGuiApplication>
19 #include <QImage>
20 #include <QPainter>
21
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};
32
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};
41
42 static void blurHorizontal(QImage &image, unsigned int *stack, int div, int radius)
43 {
44 int stackindex;
45 int stackstart;
46
47 quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
48 quint32 pixel;
49
50 int w = image.width();
51 int h = image.height();
52 int wm = w - 1;
53
54 unsigned int mulSum = stackBlur8Mul[radius];
55 unsigned int shrSum = stackBlur8Shr[radius];
56
57 unsigned int sum, sumIn, sumOut;
58
59 for (int y = 0; y < h; y++) {
60 sum = 0;
61 sumIn = 0;
62 sumOut = 0;
63
64 const int yw = y * w;
65 pixel = pixels[yw];
66 for (int i = 0; i <= radius; i++) {
67 stack[i] = qAlpha(pixel);
68
69 sum += stack[i] * (i + 1);
70 sumOut += stack[i];
71 }
72
73 for (int i = 1; i <= radius; i++) {
74 pixel = pixels[yw + qMin(i, wm)];
75
76 unsigned int *stackpix = &stack[i + radius];
77 *stackpix = qAlpha(pixel);
78
79 sum += *stackpix * (radius + 1 - i);
80 sumIn += *stackpix;
81 }
82
83 stackindex = radius;
84 for (int x = 0, i = yw; x < w; x++) {
85 pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
86
87 sum -= sumOut;
88
89 stackstart = stackindex + div - radius;
90 if (stackstart >= div) {
91 stackstart -= div;
92 }
93
94 unsigned int *stackpix = &stack[stackstart];
95
96 sumOut -= *stackpix;
97
98 pixel = pixels[yw + qMin(x + radius + 1, wm)];
99
100 *stackpix = qAlpha(pixel);
101
102 sumIn += *stackpix;
103 sum += sumIn;
104
105 if (++stackindex >= div) {
106 stackindex = 0;
107 }
108
109 stackpix = &stack[stackindex];
110
111 sumOut += *stackpix;
112 sumIn -= *stackpix;
113 }
114 }
115 }
116
117 static void blurVertical(QImage &image, unsigned int *stack, int div, int radius)
118 {
119 int stackindex;
120 int stackstart;
121
122 quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
123 quint32 pixel;
124
125 int w = image.width();
126 int h = image.height();
127 int hm = h - 1;
128
129 int mul_sum = stackBlur8Mul[radius];
130 int shr_sum = stackBlur8Shr[radius];
131
132 unsigned int sum, sumIn, sumOut;
133
134 for (int x = 0; x < w; x++) {
135 sum = 0;
136 sumIn = 0;
137 sumOut = 0;
138
139 pixel = pixels[x];
140 for (int i = 0; i <= radius; i++) {
141 stack[i] = qAlpha(pixel);
142
143 sum += stack[i] * (i + 1);
144 sumOut += stack[i];
145 }
146
147 for (int i = 1; i <= radius; i++) {
148 pixel = pixels[qMin(i, hm) * w + x];
149
150 unsigned int *stackpix = &stack[i + radius];
151 *stackpix = qAlpha(pixel);
152
153 sum += *stackpix * (radius + 1 - i);
154 sumIn += *stackpix;
155 }
156
157 stackindex = radius;
158 for (int y = 0, i = x; y < h; y++, i += w) {
159 pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
160
161 sum -= sumOut;
162
163 stackstart = stackindex + div - radius;
164 if (stackstart >= div)
165 stackstart -= div;
166
167 unsigned int *stackpix = &stack[stackstart];
168
169 sumOut -= *stackpix;
170
171 pixel = pixels[qMin(y + radius + 1, hm) * w + x];
172
173 *stackpix = qAlpha(pixel);
174
175 sumIn += *stackpix;
176 sum += sumIn;
177
178 if (++stackindex >= div) {
179 stackindex = 0;
180 }
181
182 stackpix = &stack[stackindex];
183
184 sumOut += *stackpix;
185 sumIn -= *stackpix;
186 }
187 }
188 }
189
190 static void stackBlur(QImage &image, float radius)
191 {
192 radius = qRound(radius);
193
194 int div = int(radius * 2) + 1;
195 unsigned int *stack = new unsigned int[div];
196
197 blurHorizontal(image, stack, div, radius);
198 blurVertical(image, stack, div, radius);
199
200 delete[] stack;
201 }
202
203 static void shadowBlur(QImage &image, float radius, const QColor &color)
204 {
205 if (radius < 0) {
206 return;
207 }
208
209 if (radius > 0) {
210 stackBlur(image, radius);
211 }
212
213 // Correct the color and opacity of the shadow
214 QPainter p(&image);
215 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
216 p.fillRect(image.rect(), color);
217 }
218
219 namespace
220 {
221 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
222 class TileSet
223 {
224 public:
225 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
226
227 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide, RightSide, BottomLeftCorner, BottomSide, BottomRightCorner, NumTiles };
228
229 TileSet()
230 {
231 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
232
233 QPainter p(&image);
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);
237 p.end();
238
239 shadowBlur(image, 3, Qt::black);
240
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);
250 }
251
252 void paint(QPainter *p, const QRect &r)
253 {
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]);
257 }
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]);
262 }
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]);
266 }
267 p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
268
269 const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1, -(RightMargin + 1), -(BottomMargin + 1));
270 p->fillRect(contentRect, Qt::transparent);
271 }
272
273 QPixmap m_tiles[NumTiles];
274 };
275 }
276
277 void KPixmapModifier::scale(QPixmap &pixmap, const QSize &scaledSize)
278 {
279 if (scaledSize.isEmpty() || pixmap.isNull()) {
280 pixmap = QPixmap();
281 return;
282 }
283 qreal dpr = pixmap.devicePixelRatio();
284 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
285 pixmap.setDevicePixelRatio(dpr);
286 }
287
288 void KPixmapModifier::applyFrame(QPixmap &icon, const QSize &scaledSize)
289 {
290 if (icon.isNull()) {
291 icon = QPixmap(scaledSize);
292 icon.fill(Qt::transparent);
293 return;
294 }
295
296 static TileSet tileSet;
297 qreal dpr = qApp->devicePixelRatio();
298
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);
303
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);
308
309 QPainter painter;
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);
315
316 icon = framedIcon;
317 }
318
319 QSize KPixmapModifier::sizeInsideFrame(const QSize &frameSize)
320 {
321 return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin, frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
322 }