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