Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qimageeffects.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qmath.h"
7#include "qmemrotate_p.h"
8#include "qpainter.h"
9
10#include <memory>
11
13
14namespace {
15
16template <int shift>
17inline int qt_static_shift(int value)
18{
19 if (shift == 0)
20 return value;
21 else if (shift > 0)
22 return value << (uint(shift) & 0x1f);
23 else
24 return value >> (uint(-shift) & 0x1f);
25}
26
27template<int aprec, int zprec>
28inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
29{
30 QRgb *pixel = (QRgb *)bptr;
31
32#define Z_MASK (0xff << zprec)
33 const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
34 const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
35 const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
36 const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
37#undef Z_MASK
38
39 const int zR_zprec = zR >> aprec;
40 const int zG_zprec = zG >> aprec;
41 const int zB_zprec = zB >> aprec;
42 const int zA_zprec = zA >> aprec;
43
44 zR += alpha * (R_zprec - zR_zprec);
45 zG += alpha * (G_zprec - zG_zprec);
46 zB += alpha * (B_zprec - zB_zprec);
47 zA += alpha * (A_zprec - zA_zprec);
48
49#define ZA_MASK (0xff << (zprec + aprec))
50 *pixel =
51 qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
52 | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
53 | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
54 | qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
55#undef ZA_MASK
56}
57
58const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
59
60template<int aprec, int zprec>
61inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
62{
63 const int A_zprec = int(*(bptr)) << zprec;
64 const int z_zprec = z >> aprec;
65 z += alpha * (A_zprec - z_zprec);
66 *(bptr) = z >> (zprec + aprec);
67}
68
69template<int aprec, int zprec, bool alphaOnly>
70inline void qt_blurrow(QImage & im, int line, int alpha)
71{
72 uchar *bptr = im.scanLine(line);
73
74 int zR = 0, zG = 0, zB = 0, zA = 0;
75
76 if (alphaOnly && im.format() != QImage::Format_Indexed8)
77 bptr += alphaIndex;
78
79 const int stride = im.depth() >> 3;
80 const int im_width = im.width();
81 for (int index = 0; index < im_width; ++index) {
82 if (alphaOnly)
83 qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
84 else
85 qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
86 bptr += stride;
87 }
88
89 bptr -= stride;
90
91 for (int index = im_width - 2; index >= 0; --index) {
92 bptr -= stride;
93 if (alphaOnly)
94 qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
95 else
96 qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
97 }
98}
99
100/*
101* expblur(QImage &img, int radius)
102*
103* Based on exponential blur algorithm by Jani Huhtanen
104*
105* In-place blur of image 'img' with kernel
106* of approximate radius 'radius'.
107*
108* Blurs with two sided exponential impulse
109* response.
110*
111* aprec = precision of alpha parameter
112* in fixed-point format 0.aprec
113*
114* zprec = precision of state parameters
115* zR,zG,zB and zA in fp format 8.zprec
116*/
117template <int aprec, int zprec, bool alphaOnly>
118void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
119{
120 // halve the radius if we're using two passes
121 if (improvedQuality)
122 radius *= qreal(0.5);
123
124 Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
125 || img.format() == QImage::Format_RGB32
126 || img.format() == QImage::Format_Indexed8
127 || img.format() == QImage::Format_Grayscale8);
128
129 // choose the alpha such that pixels at radius distance from a fully
130 // saturated pixel will have an alpha component of no greater than
131 // the cutOffIntensity
132 const qreal cutOffIntensity = 2;
133 int alpha = radius <= qreal(1e-5)
134 ? ((1 << aprec)-1)
135 : qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
136
137 int img_height = img.height();
138 for (int row = 0; row < img_height; ++row) {
139 for (int i = 0; i <= int(improvedQuality); ++i)
140 qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
141 }
142
143 QImage temp(img.height(), img.width(), img.format());
144 temp.setDevicePixelRatio(img.devicePixelRatio());
145 if (transposed >= 0) {
146 if (img.depth() == 8) {
147 qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
148 img.width(), img.height(), img.bytesPerLine(),
149 reinterpret_cast<quint8*>(temp.bits()),
150 temp.bytesPerLine());
151 } else {
152 qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
153 img.width(), img.height(), img.bytesPerLine(),
154 reinterpret_cast<quint32*>(temp.bits()),
155 temp.bytesPerLine());
156 }
157 } else {
158 if (img.depth() == 8) {
159 qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
160 img.width(), img.height(), img.bytesPerLine(),
161 reinterpret_cast<quint8*>(temp.bits()),
162 temp.bytesPerLine());
163 } else {
164 qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
165 img.width(), img.height(), img.bytesPerLine(),
166 reinterpret_cast<quint32*>(temp.bits()),
167 temp.bytesPerLine());
168 }
169 }
170
171 img_height = temp.height();
172 for (int row = 0; row < img_height; ++row) {
173 for (int i = 0; i <= int(improvedQuality); ++i)
174 qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
175 }
176
177 if (transposed == 0) {
178 if (img.depth() == 8) {
179 qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
180 temp.width(), temp.height(), temp.bytesPerLine(),
181 reinterpret_cast<quint8*>(img.bits()),
182 img.bytesPerLine());
183 } else {
184 qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
185 temp.width(), temp.height(), temp.bytesPerLine(),
186 reinterpret_cast<quint32*>(img.bits()),
187 img.bytesPerLine());
188 }
189 } else {
190 img = temp;
191 }
192}
193
194} // namespace
195
196#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
197#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
198
199QImage qt_halfScaled(const QImage &source)
200{
201 if (source.width() < 2 || source.height() < 2)
202 return QImage();
203
204 QImage srcImage = source;
205
206 if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
207 // assumes grayscale
208 QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
209 dest.setDevicePixelRatio(source.devicePixelRatio());
210
211 const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
212 qsizetype sx = srcImage.bytesPerLine();
213 qsizetype sx2 = sx << 1;
214
215 uchar *dst = reinterpret_cast<uchar*>(dest.bits());
216 qsizetype dx = dest.bytesPerLine();
217 int ww = dest.width();
218 int hh = dest.height();
219
220 for (int y = hh; y; --y, dst += dx, src += sx2) {
221 const uchar *p1 = src;
222 const uchar *p2 = src + sx;
223 uchar *q = dst;
224 for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
225 *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
226 }
227
228 return dest;
229 } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
230 QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
231 dest.setDevicePixelRatio(source.devicePixelRatio());
232
233 const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
234 qsizetype sx = srcImage.bytesPerLine();
235 qsizetype sx2 = sx << 1;
236
237 uchar *dst = reinterpret_cast<uchar*>(dest.bits());
238 qsizetype dx = dest.bytesPerLine();
239 int ww = dest.width();
240 int hh = dest.height();
241
242 for (int y = hh; y; --y, dst += dx, src += sx2) {
243 const uchar *p1 = src;
244 const uchar *p2 = src + sx;
245 uchar *q = dst;
246 for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
247 // alpha
248 q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
249 // rgb
250 const quint16 p16_1 = (p1[2] << 8) | p1[1];
251 const quint16 p16_2 = (p1[5] << 8) | p1[4];
252 const quint16 p16_3 = (p2[2] << 8) | p2[1];
253 const quint16 p16_4 = (p2[5] << 8) | p2[4];
254 const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
255 q[1] = result & 0xff;
256 q[2] = result >> 8;
257 }
258 }
259
260 return dest;
261 } else if (source.format() != QImage::Format_ARGB32_Premultiplied
262 && source.format() != QImage::Format_RGB32)
263 {
264 srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
265 }
266
267 QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
268 dest.setDevicePixelRatio(source.devicePixelRatio());
269
270 const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
271 qsizetype sx = srcImage.bytesPerLine() >> 2;
272 qsizetype sx2 = sx << 1;
273
274 quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
275 qsizetype dx = dest.bytesPerLine() >> 2;
276 int ww = dest.width();
277 int hh = dest.height();
278
279 for (int y = hh; y; --y, dst += dx, src += sx2) {
280 const quint32 *p1 = src;
281 const quint32 *p2 = src + sx;
282 quint32 *q = dst;
283 for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
284 *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
285 }
286
287 return dest;
288}
289
290#undef AVG
291#undef AVG16
292
293Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0)
294{
295 if (blurImage.format() != QImage::Format_ARGB32_Premultiplied
296 && blurImage.format() != QImage::Format_RGB32)
297 {
298 blurImage = std::move(blurImage).convertToFormat(QImage::Format_ARGB32_Premultiplied);
299 }
300
301 qreal scale = 1;
302 if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) {
303 blurImage = qt_halfScaled(blurImage);
304 scale = 2;
305 radius *= qreal(0.5);
306 }
307
308 if (alphaOnly)
309 expblur<12, 10, true>(blurImage, radius, quality, transposed);
310 else
311 expblur<12, 10, false>(blurImage, radius, quality, transposed);
312
313 if (p) {
314 p->scale(scale, scale);
315 p->setRenderHint(QPainter::SmoothPixmapTransform);
316 p->drawImage(QRect(QPoint(0, 0), blurImage.deviceIndependentSize().toSize()), blurImage);
317 }
318}
319
320Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0)
321{
322 if (blurImage.format() == QImage::Format_Indexed8 || blurImage.format() == QImage::Format_Grayscale8)
323 expblur<12, 10, true>(blurImage, radius, quality, transposed);
324 else
325 expblur<12, 10, false>(blurImage, radius, quality, transposed);
326}
327
328QT_END_NAMESPACE
Combined button and popup list for selecting options.
void expblur(QImage &img, qreal radius, bool improvedQuality=false, int transposed=0)
int qt_static_shift(int value)
void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
void qt_blurrow(QImage &im, int line, int alpha)
Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed=0)
#define ZA_MASK
QImage qt_halfScaled(const QImage &source)
#define AVG(a, b)
Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed=0)
#define Z_MASK
#define AVG16(a, b)