Coverage Report

Created: 2025-07-23 08:13

/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