/src/qtbase/src/gui/painting/qdrawingprimitive_sse2_p.h
Line | Count | Source (jump to first uncovered line) |
1 | | /**************************************************************************** |
2 | | ** |
3 | | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | | ** Contact: https://www.qt.io/licensing/ |
5 | | ** |
6 | | ** This file is part of the QtGui module of the Qt Toolkit. |
7 | | ** |
8 | | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | | ** Commercial License Usage |
10 | | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | | ** accordance with the commercial license agreement provided with the |
12 | | ** Software or, alternatively, in accordance with the terms contained in |
13 | | ** a written agreement between you and The Qt Company. For licensing terms |
14 | | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | | ** information use the contact form at https://www.qt.io/contact-us. |
16 | | ** |
17 | | ** GNU Lesser General Public License Usage |
18 | | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | | ** General Public License version 3 as published by the Free Software |
20 | | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | | ** packaging of this file. Please review the following information to |
22 | | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | | ** |
25 | | ** GNU General Public License Usage |
26 | | ** Alternatively, this file may be used under the terms of the GNU |
27 | | ** General Public License version 2.0 or (at your option) the GNU General |
28 | | ** Public license version 3 or any later version approved by the KDE Free |
29 | | ** Qt Foundation. The licenses are as published by the Free Software |
30 | | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | | ** included in the packaging of this file. Please review the following |
32 | | ** information to ensure the GNU General Public License requirements will |
33 | | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | | ** |
36 | | ** $QT_END_LICENSE$ |
37 | | ** |
38 | | ****************************************************************************/ |
39 | | |
40 | | #ifndef QDRAWINGPRIMITIVE_SSE2_P_H |
41 | | #define QDRAWINGPRIMITIVE_SSE2_P_H |
42 | | |
43 | | #include <QtGui/private/qtguiglobal_p.h> |
44 | | #include <private/qsimd_p.h> |
45 | | #include "qdrawhelper_x86_p.h" |
46 | | #include "qrgba64_p.h" |
47 | | |
48 | | #ifdef __SSE2__ |
49 | | |
50 | | // |
51 | | // W A R N I N G |
52 | | // ------------- |
53 | | // |
54 | | // This file is not part of the Qt API. It exists purely as an |
55 | | // implementation detail. This header file may change from version to |
56 | | // version without notice, or even be removed. |
57 | | // |
58 | | // We mean it. |
59 | | // |
60 | | |
61 | | QT_BEGIN_NAMESPACE |
62 | | |
63 | | /* |
64 | | * Multiply the components of pixelVector by alphaChannel |
65 | | * Each 32bits components of alphaChannel must be in the form 0x00AA00AA |
66 | | * colorMask must have 0x00ff00ff on each 32 bits component |
67 | | * half must have the value 128 (0x80) for each 32 bits compnent |
68 | | */ |
69 | 0 | #define BYTE_MUL_SSE2(result, pixelVector, alphaChannel, colorMask, half) \ |
70 | 0 | { \ |
71 | 0 | /* 1. separate the colors in 2 vectors so each color is on 16 bits \ |
72 | 0 | (in order to be multiplied by the alpha \ |
73 | 0 | each 32 bit of dstVectorAG are in the form 0x00AA00GG \ |
74 | 0 | each 32 bit of dstVectorRB are in the form 0x00RR00BB */\ |
75 | 0 | __m128i pixelVectorAG = _mm_srli_epi16(pixelVector, 8); \ |
76 | 0 | __m128i pixelVectorRB = _mm_and_si128(pixelVector, colorMask); \ |
77 | 0 | \ |
78 | 0 | /* 2. multiply the vectors by the alpha channel */\ |
79 | 0 | pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \ |
80 | 0 | pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \ |
81 | 0 | \ |
82 | 0 | /* 3. divide by 255, that's the tricky part. \ |
83 | 0 | we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \ |
84 | 0 | /** so first (X + X/256 + rounding) */\ |
85 | 0 | pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \ |
86 | 0 | pixelVectorRB = _mm_add_epi16(pixelVectorRB, half); \ |
87 | 0 | pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \ |
88 | 0 | pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \ |
89 | 0 | \ |
90 | 0 | /** second divide by 256 */\ |
91 | 0 | pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \ |
92 | 0 | /** for AG, we could >> 8 to divide followed by << 8 to put the \ |
93 | 0 | bytes in the correct position. By masking instead, we execute \ |
94 | 0 | only one instruction */\ |
95 | 0 | pixelVectorAG = _mm_andnot_si128(colorMask, pixelVectorAG); \ |
96 | 0 | \ |
97 | 0 | /* 4. combine the 2 pairs of colors */ \ |
98 | 0 | result = _mm_or_si128(pixelVectorAG, pixelVectorRB); \ |
99 | 0 | } |
100 | | |
101 | | /* |
102 | | * Each 32bits components of alphaChannel must be in the form 0x00AA00AA |
103 | | * oneMinusAlphaChannel must be 255 - alpha for each 32 bits component |
104 | | * colorMask must have 0x00ff00ff on each 32 bits component |
105 | | * half must have the value 128 (0x80) for each 32 bits compnent |
106 | | */ |
107 | 0 | #define INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, alphaChannel, oneMinusAlphaChannel, colorMask, half) { \ |
108 | 0 | /* interpolate AG */\ |
109 | 0 | __m128i srcVectorAG = _mm_srli_epi16(srcVector, 8); \ |
110 | 0 | __m128i dstVectorAG = _mm_srli_epi16(dstVector, 8); \ |
111 | 0 | __m128i srcVectorAGalpha = _mm_mullo_epi16(srcVectorAG, alphaChannel); \ |
112 | 0 | __m128i dstVectorAGoneMinusAlphalpha = _mm_mullo_epi16(dstVectorAG, oneMinusAlphaChannel); \ |
113 | 0 | __m128i finalAG = _mm_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlphalpha); \ |
114 | 0 | finalAG = _mm_add_epi16(finalAG, _mm_srli_epi16(finalAG, 8)); \ |
115 | 0 | finalAG = _mm_add_epi16(finalAG, half); \ |
116 | 0 | finalAG = _mm_andnot_si128(colorMask, finalAG); \ |
117 | 0 | \ |
118 | 0 | /* interpolate RB */\ |
119 | 0 | __m128i srcVectorRB = _mm_and_si128(srcVector, colorMask); \ |
120 | 0 | __m128i dstVectorRB = _mm_and_si128(dstVector, colorMask); \ |
121 | 0 | __m128i srcVectorRBalpha = _mm_mullo_epi16(srcVectorRB, alphaChannel); \ |
122 | 0 | __m128i dstVectorRBoneMinusAlphalpha = _mm_mullo_epi16(dstVectorRB, oneMinusAlphaChannel); \ |
123 | 0 | __m128i finalRB = _mm_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlphalpha); \ |
124 | 0 | finalRB = _mm_add_epi16(finalRB, _mm_srli_epi16(finalRB, 8)); \ |
125 | 0 | finalRB = _mm_add_epi16(finalRB, half); \ |
126 | 0 | finalRB = _mm_srli_epi16(finalRB, 8); \ |
127 | 0 | \ |
128 | 0 | /* combine */\ |
129 | 0 | result = _mm_or_si128(finalAG, finalRB); \ |
130 | 0 | } |
131 | | |
132 | | // same as BLEND_SOURCE_OVER_ARGB32_SSE2, but for one vector srcVector |
133 | 0 | #define BLEND_SOURCE_OVER_ARGB32_SSE2_helper(dst, srcVector, nullVector, half, one, colorMask, alphaMask) { \ |
134 | 0 | const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \ |
135 | 0 | if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \ |
136 | 0 | /* all opaque */ \ |
137 | 0 | _mm_store_si128((__m128i *)&dst[x], srcVector); \ |
138 | 0 | } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \ |
139 | 0 | /* not fully transparent */ \ |
140 | 0 | /* extract the alpha channel on 2 x 16 bits */ \ |
141 | 0 | /* so we have room for the multiplication */ \ |
142 | 0 | /* each 32 bits will be in the form 0x00AA00AA */ \ |
143 | 0 | /* with A being the 1 - alpha */ \ |
144 | 0 | __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \ |
145 | 0 | alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \ |
146 | 0 | alphaChannel = _mm_sub_epi16(one, alphaChannel); \ |
147 | 0 | \ |
148 | 0 | const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ |
149 | 0 | __m128i destMultipliedByOneMinusAlpha; \ |
150 | 0 | BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ |
151 | 0 | \ |
152 | 0 | /* result = s + d * (1-alpha) */\ |
153 | 0 | const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ |
154 | 0 | _mm_store_si128((__m128i *)&dst[x], result); \ |
155 | 0 | } \ |
156 | 0 | } |
157 | | |
158 | | |
159 | | // Basically blend src over dst with the const alpha defined as constAlphaVector. |
160 | | // nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as: |
161 | | //const __m128i nullVector = _mm_set1_epi32(0); |
162 | | //const __m128i half = _mm_set1_epi16(0x80); |
163 | | //const __m128i one = _mm_set1_epi16(0xff); |
164 | | //const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); |
165 | | //const __m128i alphaMask = _mm_set1_epi32(0xff000000); |
166 | | // |
167 | | // The computation being done is: |
168 | | // result = s + d * (1-alpha) |
169 | | // with shortcuts if fully opaque or fully transparent. |
170 | 0 | #define BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \ |
171 | 0 | int x = 0; \ |
172 | 0 | \ |
173 | 0 | /* First, get dst aligned. */ \ |
174 | 0 | ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ |
175 | 0 | blend_pixel(dst[x], src[x]); \ |
176 | 0 | } \ |
177 | 0 | \ |
178 | 0 | for (; x < length-3; x += 4) { \ |
179 | 0 | const __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[x]); \ |
180 | 0 | BLEND_SOURCE_OVER_ARGB32_SSE2_helper(dst, srcVector, nullVector, half, one, colorMask, alphaMask) \ |
181 | 0 | } \ |
182 | 0 | SIMD_EPILOGUE(x, length, 3) { \ |
183 | 0 | blend_pixel(dst[x], src[x]); \ |
184 | 0 | } \ |
185 | 0 | } |
186 | | |
187 | | // Basically blend src over dst with the const alpha defined as constAlphaVector. |
188 | | // nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as: |
189 | | //const __m128i nullVector = _mm_set1_epi32(0); |
190 | | //const __m128i half = _mm_set1_epi16(0x80); |
191 | | //const __m128i one = _mm_set1_epi16(0xff); |
192 | | //const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); |
193 | | // |
194 | | // The computation being done is: |
195 | | // dest = (s + d * sia) * ca + d * cia |
196 | | // = s * ca + d * (sia * ca + cia) |
197 | | // = s * ca + d * (1 - sa*ca) |
198 | 0 | #define BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector) \ |
199 | 0 | { \ |
200 | 0 | int x = 0; \ |
201 | 0 | \ |
202 | 0 | ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \ |
203 | 0 | blend_pixel(dst[x], src[x], const_alpha); \ |
204 | 0 | } \ |
205 | 0 | \ |
206 | 0 | for (; x < length-3; x += 4) { \ |
207 | 0 | __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[x]); \ |
208 | 0 | if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { \ |
209 | 0 | BYTE_MUL_SSE2(srcVector, srcVector, constAlphaVector, colorMask, half); \ |
210 | 0 | \ |
211 | 0 | __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \ |
212 | 0 | alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \ |
213 | 0 | alphaChannel = _mm_sub_epi16(one, alphaChannel); \ |
214 | 0 | \ |
215 | 0 | const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \ |
216 | 0 | __m128i destMultipliedByOneMinusAlpha; \ |
217 | 0 | BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \ |
218 | 0 | \ |
219 | 0 | const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \ |
220 | 0 | _mm_store_si128((__m128i *)&dst[x], result); \ |
221 | 0 | } \ |
222 | 0 | } \ |
223 | 0 | SIMD_EPILOGUE(x, length, 3) { \ |
224 | 0 | blend_pixel(dst[x], src[x], const_alpha); \ |
225 | 0 | } \ |
226 | 0 | } |
227 | | |
228 | | QT_END_NAMESPACE |
229 | | |
230 | | #endif // __SSE2__ |
231 | | |
232 | | QT_BEGIN_NAMESPACE |
233 | | #if QT_COMPILER_SUPPORTS_HERE(SSE4_1) |
234 | | QT_FUNCTION_TARGET(SSE2) |
235 | | Q_ALWAYS_INLINE void Q_DECL_VECTORCALL reciprocal_mul_ss(__m128 &ia, const __m128 a, float mul) |
236 | 0 | { |
237 | 0 | ia = _mm_rcp_ss(a); // Approximate 1/a |
238 | | // Improve precision of ia using Newton-Raphson |
239 | 0 | ia = _mm_sub_ss(_mm_add_ss(ia, ia), _mm_mul_ss(ia, _mm_mul_ss(ia, a))); |
240 | 0 | ia = _mm_mul_ss(ia, _mm_set_ss(mul)); |
241 | 0 | ia = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0,0,0,0)); |
242 | 0 | } |
243 | | |
244 | | QT_FUNCTION_TARGET(SSE4_1) |
245 | | inline QRgb qUnpremultiply_sse4(QRgb p) |
246 | 0 | { |
247 | 0 | const uint alpha = qAlpha(p); |
248 | 0 | if (alpha == 255) |
249 | 0 | return p; |
250 | 0 | if (alpha == 0) |
251 | 0 | return 0; |
252 | 0 | const __m128 va = _mm_set1_ps(alpha); |
253 | 0 | __m128 via; |
254 | 0 | reciprocal_mul_ss(via, va, 255.0f); // Approximate 1/a |
255 | 0 | __m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p)); |
256 | 0 | vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via)); |
257 | 0 | vl = _mm_packus_epi32(vl, vl); |
258 | 0 | vl = _mm_insert_epi16(vl, alpha, 3); |
259 | 0 | vl = _mm_packus_epi16(vl, vl); |
260 | 0 | return _mm_cvtsi128_si32(vl); |
261 | 0 | } |
262 | | |
263 | | template<enum QtPixelOrder PixelOrder> |
264 | | QT_FUNCTION_TARGET(SSE4_1) |
265 | | inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p) |
266 | 0 | { |
267 | 0 | const uint alpha = qAlpha(p); |
268 | 0 | if (alpha == 255) |
269 | 0 | return qConvertRgb32ToRgb30<PixelOrder>(p); |
270 | 0 | if (alpha == 0) |
271 | 0 | return 0; |
272 | 0 | Q_CONSTEXPR float mult = 1023.0f / (255 >> 6); |
273 | 0 | const uint newalpha = (alpha >> 6); |
274 | 0 | const __m128 va = _mm_set1_ps(alpha); |
275 | 0 | __m128 via; |
276 | 0 | reciprocal_mul_ss(via, va, mult * newalpha); |
277 | 0 | __m128i vl = _mm_cvtsi32_si128(p); |
278 | 0 | vl = _mm_cvtepu8_epi32(vl); |
279 | 0 | vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl), via)); |
280 | 0 | vl = _mm_packus_epi32(vl, vl); |
281 | 0 | uint rgb30 = (newalpha << 30); |
282 | 0 | rgb30 |= ((uint)_mm_extract_epi16(vl, 1)) << 10; |
283 | 0 | if (PixelOrder == PixelOrderRGB) { |
284 | 0 | rgb30 |= ((uint)_mm_extract_epi16(vl, 2)) << 20; |
285 | 0 | rgb30 |= ((uint)_mm_extract_epi16(vl, 0)); |
286 | 0 | } else { |
287 | 0 | rgb30 |= ((uint)_mm_extract_epi16(vl, 0)) << 20; |
288 | 0 | rgb30 |= ((uint)_mm_extract_epi16(vl, 2)); |
289 | 0 | } |
290 | 0 | return rgb30; |
291 | 0 | } Unexecuted instantiation: unsigned int qConvertArgb32ToA2rgb30_sse4<(QtPixelOrder)1>(unsigned int) Unexecuted instantiation: unsigned int qConvertArgb32ToA2rgb30_sse4<(QtPixelOrder)0>(unsigned int) |
292 | | |
293 | | template<enum QtPixelOrder PixelOrder> |
294 | | QT_FUNCTION_TARGET(SSE4_1) |
295 | | inline uint qConvertRgba64ToRgb32_sse4(QRgba64 p) |
296 | 0 | { |
297 | 0 | if (p.isTransparent()) |
298 | 0 | return 0; |
299 | 0 | __m128i vl = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&p)); |
300 | 0 | if (!p.isOpaque()) { |
301 | 0 | const __m128 va = _mm_set1_ps(p.alpha()); |
302 | 0 | __m128 via; |
303 | 0 | reciprocal_mul_ss(via, va, 65535.0f); |
304 | 0 | vl = _mm_unpacklo_epi16(vl, _mm_setzero_si128()); |
305 | 0 | vl = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(vl) , via)); |
306 | 0 | vl = _mm_packus_epi32(vl, vl); |
307 | 0 | vl = _mm_insert_epi16(vl, p.alpha(), 3); |
308 | 0 | } |
309 | 0 | if (PixelOrder == PixelOrderBGR) |
310 | 0 | vl = _mm_shufflelo_epi16(vl, _MM_SHUFFLE(3, 0, 1, 2)); |
311 | 0 | return toArgb32(vl); |
312 | 0 | } Unexecuted instantiation: unsigned int qConvertRgba64ToRgb32_sse4<(QtPixelOrder)1>(QRgba64) Unexecuted instantiation: unsigned int qConvertRgba64ToRgb32_sse4<(QtPixelOrder)0>(QRgba64) |
313 | | #endif |
314 | | QT_END_NAMESPACE |
315 | | |
316 | | #endif // QDRAWINGPRIMITIVE_SSE2_P_H |