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 "imagefilter_p.h"
17
18#include <QColor>
19#include <QImage>
20#include <QPainter>
21
22#include <cmath>
23#include <string.h>
24
25using namespace KIO;
26
27static const quint32 stack_blur8_mul[255] = {
28 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,
29 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,
30 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,
31 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,
32 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,
33 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,
34 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,
35 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,
36 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259};
37
38static const quint32 stack_blur8_shr[255] = {
39 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,
40 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,
41 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,
42 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,
43 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,
44 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,
45 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};
46
47inline static void blurHorizontal(QImage &image, unsigned int *stack, int div, int radius)
48{
49 quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
50 quint32 pixel = 0;
51
52 const int width = image.width();
53 const int height = image.height();
54 const int wm = width - 1;
55
56 const unsigned int mul_sum = stack_blur8_mul[radius];
57 const unsigned int shr_sum = stack_blur8_shr[radius];
58
59 for (int y = 0; y < height; y++) {
60 unsigned int sum = 0;
61 unsigned int sum_in = 0;
62 unsigned int sum_out = 0;
63
64 const int yw = y * width;
65 pixel = pixels[yw];
66 for (int i = 0; i <= radius; i++) {
67 stack[i] = qAlpha(rgb: pixel);
68
69 sum += stack[i] * (i + 1);
70 sum_out += stack[i];
71 }
72
73 for (int i = 1; i <= radius; i++) {
74 pixel = pixels[yw + qMin(a: i, b: wm)];
75
76 unsigned int *stackpix = &stack[i + radius];
77 *stackpix = qAlpha(rgb: pixel);
78
79 sum += *stackpix * (radius + 1 - i);
80 sum_in += *stackpix;
81 }
82
83 int stackindex = radius;
84 for (int x = 0, i = yw; x < width; x++) {
85 pixels[i++] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
86
87 sum -= sum_out;
88
89 int stackstart = stackindex + div - radius;
90 if (stackstart >= div) {
91 stackstart -= div;
92 }
93
94 unsigned int *stackpix = &stack[stackstart];
95
96 sum_out -= *stackpix;
97
98 pixel = pixels[yw + qMin(a: x + radius + 1, b: wm)];
99
100 *stackpix = qAlpha(rgb: pixel);
101
102 sum_in += *stackpix;
103 sum += sum_in;
104
105 if (++stackindex >= div) {
106 stackindex = 0;
107 }
108
109 stackpix = &stack[stackindex];
110
111 sum_out += *stackpix;
112 sum_in -= *stackpix;
113 } // for (x = 0, ...)
114 } // for (y = 0, ...)
115}
116
117inline 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 = stack_blur8_mul[radius];
130 int shr_sum = stack_blur8_shr[radius];
131
132 unsigned int sum;
133 unsigned int sum_in;
134 unsigned int sum_out;
135
136 for (int x = 0; x < w; x++) {
137 sum = 0;
138 sum_in = 0;
139 sum_out = 0;
140
141 pixel = pixels[x];
142 for (int i = 0; i <= radius; i++) {
143 stack[i] = qAlpha(rgb: pixel);
144
145 sum += stack[i] * (i + 1);
146 sum_out += stack[i];
147 }
148
149 for (int i = 1; i <= radius; i++) {
150 pixel = pixels[qMin(a: i, b: hm) * w + x];
151
152 unsigned int *stackpix = &stack[i + radius];
153 *stackpix = qAlpha(rgb: pixel);
154
155 sum += *stackpix * (radius + 1 - i);
156 sum_in += *stackpix;
157 }
158
159 stackindex = radius;
160 for (int y = 0, i = x; y < h; y++, i += w) {
161 pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
162
163 sum -= sum_out;
164
165 stackstart = stackindex + div - radius;
166 if (stackstart >= div) {
167 stackstart -= div;
168 }
169
170 unsigned int *stackpix = &stack[stackstart];
171
172 sum_out -= *stackpix;
173
174 pixel = pixels[qMin(a: y + radius + 1, b: hm) * w + x];
175
176 *stackpix = qAlpha(rgb: pixel);
177
178 sum_in += *stackpix;
179 sum += sum_in;
180
181 if (++stackindex >= div) {
182 stackindex = 0;
183 }
184
185 stackpix = &stack[stackindex];
186
187 sum_out += *stackpix;
188 sum_in -= *stackpix;
189 } // for (y = 0, ...)
190 } // for (x = 0, ...)
191}
192
193static void stackBlur(QImage &image, float radius)
194{
195 radius = qRound(f: radius);
196
197 int div = int(radius * 2) + 1;
198 unsigned int *stack = new unsigned int[div];
199
200 blurHorizontal(image, stack, div, radius);
201 blurVertical(image, stack, div, radius);
202
203 delete[] stack;
204}
205
206void ImageFilter::shadowBlur(QImage &image, float radius, const QColor &color)
207{
208 if (radius < 0) {
209 return;
210 }
211
212 if (radius > 0) {
213 stackBlur(image, radius);
214 }
215
216 // Correct the color and opacity of the shadow
217 QPainter p(&image);
218 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
219 p.fillRect(image.rect(), color);
220}
221

source code of kio/src/widgets/imagefilter.cpp