1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifndef QDRAWINGPRIMITIVE_SSE2_P_H
43 #define QDRAWINGPRIMITIVE_SSE2_P_H
44 
45 #include <private/qsimd_p.h>
46 
47 #ifdef QT_HAVE_SSE2
48 
49 //
50 //  W A R N I N G
51 //  -------------
52 //
53 // This file is not part of the Qt API.  It exists purely as an
54 // implementation detail.  This header file may change from version to
55 // version without notice, or even be removed.
56 //
57 // We mean it.
58 //
59 
60 QT_BEGIN_NAMESPACE
61 
62 /*
63  * Multiply the components of pixelVector by alphaChannel
64  * Each 32bits components of alphaChannel must be in the form 0x00AA00AA
65  * colorMask must have 0x00ff00ff on each 32 bits component
66  * half must have the value 128 (0x80) for each 32 bits compnent
67  */
68 #define BYTE_MUL_SSE2(result, pixelVector, alphaChannel, colorMask, half) \
69 { \
70     /* 1. separate the colors in 2 vectors so each color is on 16 bits \
71        (in order to be multiplied by the alpha \
72        each 32 bit of dstVectorAG are in the form 0x00AA00GG \
73        each 32 bit of dstVectorRB are in the form 0x00RR00BB */\
74     __m128i pixelVectorAG = _mm_srli_epi16(pixelVector, 8); \
75     __m128i pixelVectorRB = _mm_and_si128(pixelVector, colorMask); \
76  \
77     /* 2. multiply the vectors by the alpha channel */\
78     pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \
79     pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \
80  \
81     /* 3. divide by 255, that's the tricky part. \
82        we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \
83     /** so first (X + X/256 + rounding) */\
84     pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \
85     pixelVectorRB = _mm_add_epi16(pixelVectorRB, half); \
86     pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \
87     pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \
88  \
89     /** second divide by 256 */\
90     pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \
91     /** for AG, we could >> 8 to divide followed by << 8 to put the \
92         bytes in the correct position. By masking instead, we execute \
93         only one instruction */\
94     pixelVectorAG = _mm_andnot_si128(colorMask, pixelVectorAG); \
95  \
96     /* 4. combine the 2 pairs of colors */ \
97     result = _mm_or_si128(pixelVectorAG, pixelVectorRB); \
98 }
99 
100 /*
101  * Each 32bits components of alphaChannel must be in the form 0x00AA00AA
102  * oneMinusAlphaChannel must be 255 - alpha for each 32 bits component
103  * colorMask must have 0x00ff00ff on each 32 bits component
104  * half must have the value 128 (0x80) for each 32 bits compnent
105  */
106 #define INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, alphaChannel, oneMinusAlphaChannel, colorMask, half) { \
107     /* interpolate AG */\
108     __m128i srcVectorAG = _mm_srli_epi16(srcVector, 8); \
109     __m128i dstVectorAG = _mm_srli_epi16(dstVector, 8); \
110     __m128i srcVectorAGalpha = _mm_mullo_epi16(srcVectorAG, alphaChannel); \
111     __m128i dstVectorAGoneMinusAlphalpha = _mm_mullo_epi16(dstVectorAG, oneMinusAlphaChannel); \
112     __m128i finalAG = _mm_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlphalpha); \
113     finalAG = _mm_add_epi16(finalAG, _mm_srli_epi16(finalAG, 8)); \
114     finalAG = _mm_add_epi16(finalAG, half); \
115     finalAG = _mm_andnot_si128(colorMask, finalAG); \
116  \
117     /* interpolate RB */\
118     __m128i srcVectorRB = _mm_and_si128(srcVector, colorMask); \
119     __m128i dstVectorRB = _mm_and_si128(dstVector, colorMask); \
120     __m128i srcVectorRBalpha = _mm_mullo_epi16(srcVectorRB, alphaChannel); \
121     __m128i dstVectorRBoneMinusAlphalpha = _mm_mullo_epi16(dstVectorRB, oneMinusAlphaChannel); \
122     __m128i finalRB = _mm_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlphalpha); \
123     finalRB = _mm_add_epi16(finalRB, _mm_srli_epi16(finalRB, 8)); \
124     finalRB = _mm_add_epi16(finalRB, half); \
125     finalRB = _mm_srli_epi16(finalRB, 8); \
126  \
127     /* combine */\
128     result = _mm_or_si128(finalAG, finalRB); \
129 }
130 
131 // Basically blend src over dst with the const alpha defined as constAlphaVector.
132 // nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as:
133 //const __m128i nullVector = _mm_set1_epi32(0);
134 //const __m128i half = _mm_set1_epi16(0x80);
135 //const __m128i one = _mm_set1_epi16(0xff);
136 //const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
137 //const __m128i alphaMask = _mm_set1_epi32(0xff000000);
138 //
139 // The computation being done is:
140 // result = s + d * (1-alpha)
141 // with shortcuts if fully opaque or fully transparent.
142 #define BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \
143     int x = 0; \
144 \
145     /* First, get dst aligned. */ \
146     ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
147         uint s = src[x]; \
148         if (s >= 0xff000000) \
149             dst[x] = s; \
150         else if (s != 0) \
151             dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
152     } \
153 \
154     for (; x < length-3; x += 4) { \
155         const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \
156         const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \
157         if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \
158             /* all opaque */ \
159             _mm_store_si128((__m128i *)&dst[x], srcVector); \
160         } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \
161             /* not fully transparent */ \
162             /* extract the alpha channel on 2 x 16 bits */ \
163             /* so we have room for the multiplication */ \
164             /* each 32 bits will be in the form 0x00AA00AA */ \
165             /* with A being the 1 - alpha */ \
166             __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \
167             alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \
168             alphaChannel = _mm_sub_epi16(one, alphaChannel); \
169  \
170             const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
171             __m128i destMultipliedByOneMinusAlpha; \
172             BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
173  \
174             /* result = s + d * (1-alpha) */\
175             const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
176             _mm_store_si128((__m128i *)&dst[x], result); \
177         } \
178     } \
179     for (; x < length; ++x) { \
180         uint s = src[x]; \
181         if (s >= 0xff000000) \
182             dst[x] = s; \
183         else if (s != 0) \
184             dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
185     } \
186 }
187 
188 // Basically blend src over dst with the const alpha defined as constAlphaVector.
189 // nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as:
190 //const __m128i nullVector = _mm_set1_epi32(0);
191 //const __m128i half = _mm_set1_epi16(0x80);
192 //const __m128i one = _mm_set1_epi16(0xff);
193 //const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
194 //
195 // The computation being done is:
196 // dest = (s + d * sia) * ca + d * cia
197 //      = s * ca + d * (sia * ca + cia)
198 //      = s * ca + d * (1 - sa*ca)
199 #define BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector) \
200 { \
201     int x = 0; \
202 \
203     ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
204         quint32 s = src[x]; \
205         if (s != 0) { \
206             s = BYTE_MUL(s, const_alpha); \
207             dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
208         } \
209     } \
210 \
211     for (; x < length-3; x += 4) { \
212         __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \
213         if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { \
214             BYTE_MUL_SSE2(srcVector, srcVector, constAlphaVector, colorMask, half); \
215 \
216             __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \
217             alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \
218             alphaChannel = _mm_sub_epi16(one, alphaChannel); \
219  \
220             const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
221             __m128i destMultipliedByOneMinusAlpha; \
222             BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
223  \
224             const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
225             _mm_store_si128((__m128i *)&dst[x], result); \
226         } \
227     } \
228     for (; x < length; ++x) { \
229         quint32 s = src[x]; \
230         if (s != 0) { \
231             s = BYTE_MUL(s, const_alpha); \
232             dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
233         } \
234     } \
235 }
236 
237 QT_END_NAMESPACE
238 
239 #endif // QT_HAVE_SSE2
240 
241 #endif // QDRAWINGPRIMITIVE_SSE2_P_H
242