FreeRDP
Loading...
Searching...
No Matches
prim_YUV_sse4.1.c
1
23#include <winpr/wtypes.h>
24#include <freerdp/config.h>
25
26#include <winpr/sysinfo.h>
27#include <winpr/crt.h>
28#include <freerdp/types.h>
29#include <freerdp/primitives.h>
30
31#include "prim_internal.h"
32#include "prim_avxsse.h"
33#include "prim_YUV.h"
34
35#if defined(SSE_AVX_INTRINSICS_ENABLED)
36#include <emmintrin.h>
37#include <tmmintrin.h>
38#include <smmintrin.h>
39
40static primitives_t* generic = nullptr;
41
42/****************************************************************************/
43/* sse41 YUV420 -> RGB conversion */
44/****************************************************************************/
45static inline __m128i* sse41_YUV444Pixel(__m128i* WINPR_RESTRICT dst, __m128i Yraw, __m128i Uraw,
46 __m128i Vraw, UINT8 pos)
47{
48 const __m128i mapY[] = { mm_set_epu32(0x80800380, 0x80800280, 0x80800180, 0x80800080),
49 mm_set_epu32(0x80800780, 0x80800680, 0x80800580, 0x80800480),
50 mm_set_epu32(0x80800B80, 0x80800A80, 0x80800980, 0x80800880),
51 mm_set_epu32(0x80800F80, 0x80800E80, 0x80800D80, 0x80800C80) };
52 const __m128i mapUV[] = { mm_set_epu32(0x80038002, 0x80018000, 0x80808080, 0x80808080),
53 mm_set_epu32(0x80078006, 0x80058004, 0x80808080, 0x80808080),
54 mm_set_epu32(0x800B800A, 0x80098008, 0x80808080, 0x80808080),
55 mm_set_epu32(0x800F800E, 0x800D800C, 0x80808080, 0x80808080) };
56 const __m128i mask[] = { mm_set_epu32(0x80038080, 0x80028080, 0x80018080, 0x80008080),
57 mm_set_epu32(0x80800380, 0x80800280, 0x80800180, 0x80800080),
58 mm_set_epu32(0x80808003, 0x80808002, 0x80808001, 0x80808000) };
59 const __m128i c128 = _mm_set1_epi16(128);
60 __m128i BGRX = _mm_and_si128(LOAD_SI128(dst),
61 mm_set_epu32(0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000));
62 {
63 __m128i C;
64 __m128i D;
65 __m128i E;
66 /* Load Y values and expand to 32 bit */
67 {
68 C = _mm_shuffle_epi8(Yraw, mapY[pos]); /* Reorder and multiply by 256 */
69 }
70 /* Load U values and expand to 32 bit */
71 {
72 const __m128i U = _mm_shuffle_epi8(Uraw, mapUV[pos]); /* Reorder dcba */
73 D = _mm_sub_epi16(U, c128); /* D = U - 128 */
74 }
75 /* Load V values and expand to 32 bit */
76 {
77 const __m128i V = _mm_shuffle_epi8(Vraw, mapUV[pos]); /* Reorder dcba */
78 E = _mm_sub_epi16(V, c128); /* E = V - 128 */
79 }
80 /* Get the R value */
81 {
82 const __m128i c403 = _mm_set1_epi16(403);
83 const __m128i e403 =
84 _mm_unpackhi_epi16(_mm_mullo_epi16(E, c403), _mm_mulhi_epi16(E, c403));
85 const __m128i Rs = _mm_add_epi32(C, e403);
86 const __m128i R32 = _mm_srai_epi32(Rs, 8);
87 const __m128i R16 = _mm_packs_epi32(R32, _mm_setzero_si128());
88 const __m128i R = _mm_packus_epi16(R16, _mm_setzero_si128());
89 const __m128i packed = _mm_shuffle_epi8(R, mask[0]);
90 BGRX = _mm_or_si128(BGRX, packed);
91 }
92 /* Get the G value */
93 {
94 const __m128i c48 = _mm_set1_epi16(48);
95 const __m128i d48 =
96 _mm_unpackhi_epi16(_mm_mullo_epi16(D, c48), _mm_mulhi_epi16(D, c48));
97 const __m128i c120 = _mm_set1_epi16(120);
98 const __m128i e120 =
99 _mm_unpackhi_epi16(_mm_mullo_epi16(E, c120), _mm_mulhi_epi16(E, c120));
100 const __m128i de = _mm_add_epi32(d48, e120);
101 const __m128i Gs = _mm_sub_epi32(C, de);
102 const __m128i G32 = _mm_srai_epi32(Gs, 8);
103 const __m128i G16 = _mm_packs_epi32(G32, _mm_setzero_si128());
104 const __m128i G = _mm_packus_epi16(G16, _mm_setzero_si128());
105 const __m128i packed = _mm_shuffle_epi8(G, mask[1]);
106 BGRX = _mm_or_si128(BGRX, packed);
107 }
108 /* Get the B value */
109 {
110 const __m128i c475 = _mm_set1_epi16(475);
111 const __m128i d475 =
112 _mm_unpackhi_epi16(_mm_mullo_epi16(D, c475), _mm_mulhi_epi16(D, c475));
113 const __m128i Bs = _mm_add_epi32(C, d475);
114 const __m128i B32 = _mm_srai_epi32(Bs, 8);
115 const __m128i B16 = _mm_packs_epi32(B32, _mm_setzero_si128());
116 const __m128i B = _mm_packus_epi16(B16, _mm_setzero_si128());
117 const __m128i packed = _mm_shuffle_epi8(B, mask[2]);
118 BGRX = _mm_or_si128(BGRX, packed);
119 }
120 }
121 STORE_SI128(dst++, BGRX);
122 return dst;
123}
124
125static inline pstatus_t sse41_YUV420ToRGB_BGRX(const BYTE* WINPR_RESTRICT pSrc[],
126 const UINT32* WINPR_RESTRICT srcStep,
127 BYTE* WINPR_RESTRICT pDst, UINT32 dstStep,
128 const prim_size_t* WINPR_RESTRICT roi)
129{
130 const UINT32 nWidth = roi->width;
131 const UINT32 nHeight = roi->height;
132 const UINT32 pad = roi->width % 16;
133 const __m128i duplicate = _mm_set_epi8(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0);
134
135 for (size_t y = 0; y < nHeight; y++)
136 {
137 __m128i* dst = WINPR_PACKED_ALIGN_CAST(__m128i*, (pDst + dstStep * y));
138 const BYTE* YData = pSrc[0] + y * srcStep[0];
139 const BYTE* UData = pSrc[1] + (y / 2) * srcStep[1];
140 const BYTE* VData = pSrc[2] + (y / 2) * srcStep[2];
141
142 for (UINT32 x = 0; x < nWidth - pad; x += 16)
143 {
144 const __m128i Y = LOAD_SI128(YData);
145 const __m128i uRaw = LOAD_SI128(UData);
146 const __m128i vRaw = LOAD_SI128(VData);
147 const __m128i U = _mm_shuffle_epi8(uRaw, duplicate);
148 const __m128i V = _mm_shuffle_epi8(vRaw, duplicate);
149 YData += 16;
150 UData += 8;
151 VData += 8;
152 dst = sse41_YUV444Pixel(dst, Y, U, V, 0);
153 dst = sse41_YUV444Pixel(dst, Y, U, V, 1);
154 dst = sse41_YUV444Pixel(dst, Y, U, V, 2);
155 dst = sse41_YUV444Pixel(dst, Y, U, V, 3);
156 }
157
158 for (UINT32 x = 0; x < pad; x++)
159 {
160 const BYTE Y = *YData++;
161 const BYTE U = *UData;
162 const BYTE V = *VData;
163 dst = WINPR_PACKED_ALIGN_CAST(
164 __m128i*, writeYUVPixel((BYTE*)dst, PIXEL_FORMAT_BGRX32, Y, U, V, writePixelBGRX));
165
166 if (x % 2)
167 {
168 UData++;
169 VData++;
170 }
171 }
172 }
173
174 return PRIMITIVES_SUCCESS;
175}
176
177static pstatus_t sse41_YUV420ToRGB(const BYTE* WINPR_RESTRICT pSrc[3], const UINT32 srcStep[3],
178 BYTE* WINPR_RESTRICT pDst, UINT32 dstStep, UINT32 DstFormat,
179 const prim_size_t* WINPR_RESTRICT roi)
180{
181 switch (DstFormat)
182 {
183 case PIXEL_FORMAT_BGRX32:
184 case PIXEL_FORMAT_BGRA32:
185 return sse41_YUV420ToRGB_BGRX(pSrc, srcStep, pDst, dstStep, roi);
186
187 default:
188 return generic->YUV420ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi);
189 }
190}
191
192static inline void BGRX_fillRGB(size_t offset, BYTE* WINPR_RESTRICT pRGB[2],
193 const BYTE* WINPR_RESTRICT pY[2], const BYTE* WINPR_RESTRICT pU[2],
194 const BYTE* WINPR_RESTRICT pV[2], BOOL filter)
195{
196 WINPR_ASSERT(pRGB);
197 WINPR_ASSERT(pY);
198 WINPR_ASSERT(pU);
199 WINPR_ASSERT(pV);
200
201 const UINT32 DstFormat = PIXEL_FORMAT_BGRX32;
202 const UINT32 bpp = 4;
203
204 for (size_t i = 0; i < 2; i++)
205 {
206 for (size_t j = 0; j < 2; j++)
207 {
208 const BYTE Y = pY[i][offset + j];
209 BYTE U = pU[i][offset + j];
210 BYTE V = pV[i][offset + j];
211 if ((i == 0) && (j == 0) && filter)
212 {
213 const INT32 avgU =
214 4 * pU[0][offset] - pU[0][offset + 1] - pU[1][offset] - pU[1][offset + 1];
215 const INT32 avgV =
216 4 * pV[0][offset] - pV[0][offset + 1] - pV[1][offset] - pV[1][offset + 1];
217
218 U = CONDITIONAL_CLIP(avgU, pU[0][offset]);
219 V = CONDITIONAL_CLIP(avgV, pV[0][offset]);
220 }
221
222 writeYUVPixel(&pRGB[i][(j + offset) * bpp], DstFormat, Y, U, V, writePixelBGRX);
223 }
224 }
225}
226
227/* input are uint16_t vectors */
228static inline __m128i sse41_yuv2x_single(const __m128i Y, __m128i U, __m128i V, const short iMulU,
229 const short iMulV)
230{
231 const __m128i zero = _mm_set1_epi8(0);
232
233 __m128i Ylo = _mm_unpacklo_epi16(Y, zero);
234 __m128i Yhi = _mm_unpackhi_epi16(Y, zero);
235 if (iMulU != 0)
236 {
237 const __m128i addX = _mm_set1_epi16(128);
238 const __m128i D = _mm_sub_epi16(U, addX);
239 const __m128i mulU = _mm_set1_epi16(iMulU);
240 const __m128i mulDlo = _mm_mullo_epi16(D, mulU);
241 const __m128i mulDhi = _mm_mulhi_epi16(D, mulU);
242 const __m128i Dlo = _mm_unpacklo_epi16(mulDlo, mulDhi);
243 Ylo = _mm_add_epi32(Ylo, Dlo);
244
245 const __m128i Dhi = _mm_unpackhi_epi16(mulDlo, mulDhi);
246 Yhi = _mm_add_epi32(Yhi, Dhi);
247 }
248 if (iMulV != 0)
249 {
250 const __m128i addX = _mm_set1_epi16(128);
251 const __m128i E = _mm_sub_epi16(V, addX);
252 const __m128i mul = _mm_set1_epi16(iMulV);
253 const __m128i mulElo = _mm_mullo_epi16(E, mul);
254 const __m128i mulEhi = _mm_mulhi_epi16(E, mul);
255 const __m128i Elo = _mm_unpacklo_epi16(mulElo, mulEhi);
256 const __m128i esumlo = _mm_add_epi32(Ylo, Elo);
257
258 const __m128i Ehi = _mm_unpackhi_epi16(mulElo, mulEhi);
259 const __m128i esumhi = _mm_add_epi32(Yhi, Ehi);
260 Ylo = esumlo;
261 Yhi = esumhi;
262 }
263
264 const __m128i rYlo = _mm_srai_epi32(Ylo, 8);
265 const __m128i rYhi = _mm_srai_epi32(Yhi, 8);
266 const __m128i rY = _mm_packs_epi32(rYlo, rYhi);
267 return rY;
268}
269
270/* Input are uint8_t vectors */
271static inline __m128i sse41_yuv2x(const __m128i Y, __m128i U, __m128i V, const short iMulU,
272 const short iMulV)
273{
274 const __m128i zero = _mm_set1_epi8(0);
275
276 /* Ylo = Y * 256
277 * Ulo = uint8_t -> uint16_t
278 * Vlo = uint8_t -> uint16_t
279 */
280 const __m128i Ylo = _mm_unpacklo_epi8(zero, Y);
281 const __m128i Ulo = _mm_unpacklo_epi8(U, zero);
282 const __m128i Vlo = _mm_unpacklo_epi8(V, zero);
283 const __m128i preslo = sse41_yuv2x_single(Ylo, Ulo, Vlo, iMulU, iMulV);
284
285 const __m128i Yhi = _mm_unpackhi_epi8(zero, Y);
286 const __m128i Uhi = _mm_unpackhi_epi8(U, zero);
287 const __m128i Vhi = _mm_unpackhi_epi8(V, zero);
288 const __m128i preshi = sse41_yuv2x_single(Yhi, Uhi, Vhi, iMulU, iMulV);
289 const __m128i res = _mm_packus_epi16(preslo, preshi);
290
291 return res;
292}
293
294/* const INT32 r = ((256L * C(Y) + 0L * D(U) + 403L * E(V))) >> 8; */
295static inline __m128i sse41_yuv2r(const __m128i Y, __m128i U, __m128i V)
296{
297 return sse41_yuv2x(Y, U, V, 0, 403);
298}
299
300/* const INT32 g = ((256L * C(Y) - 48L * D(U) - 120L * E(V))) >> 8; */
301static inline __m128i sse41_yuv2g(const __m128i Y, __m128i U, __m128i V)
302{
303 return sse41_yuv2x(Y, U, V, -48, -120);
304}
305
306/* const INT32 b = ((256L * C(Y) + 475L * D(U) + 0L * E(V))) >> 8; */
307static inline __m128i sse41_yuv2b(const __m128i Y, __m128i U, __m128i V)
308{
309 return sse41_yuv2x(Y, U, V, 475, 0);
310}
311
312static inline void sse41_BGRX_fillRGB_pixel(BYTE* WINPR_RESTRICT pRGB, __m128i Y, __m128i U,
313 __m128i V)
314{
315 const __m128i zero = _mm_set1_epi8(0);
316 /* Y * 256 */
317 const __m128i r = sse41_yuv2r(Y, U, V);
318 const __m128i rx[2] = { _mm_unpackhi_epi8(r, zero), _mm_unpacklo_epi8(r, zero) };
319
320 const __m128i g = sse41_yuv2g(Y, U, V);
321 const __m128i b = sse41_yuv2b(Y, U, V);
322
323 const __m128i bg[2] = { _mm_unpackhi_epi8(b, g), _mm_unpacklo_epi8(b, g) };
324
325 const __m128i mask = mm_set_epu8(0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
326 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF);
327
328 __m128i* rgb = WINPR_PACKED_ALIGN_CAST(__m128i*, pRGB);
329 const __m128i bgrx0 = _mm_unpacklo_epi16(bg[1], rx[1]);
330 _mm_maskmoveu_si128(bgrx0, mask, (char*)&rgb[0]);
331 const __m128i bgrx1 = _mm_unpackhi_epi16(bg[1], rx[1]);
332 _mm_maskmoveu_si128(bgrx1, mask, (char*)&rgb[1]);
333 const __m128i bgrx2 = _mm_unpacklo_epi16(bg[0], rx[0]);
334 _mm_maskmoveu_si128(bgrx2, mask, (char*)&rgb[2]);
335 const __m128i bgrx3 = _mm_unpackhi_epi16(bg[0], rx[0]);
336 _mm_maskmoveu_si128(bgrx3, mask, (char*)&rgb[3]);
337}
338
339static inline __m128i odd1sum(__m128i u1)
340{
341 const __m128i zero = _mm_set1_epi8(0);
342 const __m128i u1hi = _mm_unpackhi_epi8(u1, zero);
343 const __m128i u1lo = _mm_unpacklo_epi8(u1, zero);
344 return _mm_hadds_epi16(u1lo, u1hi);
345}
346
347static inline __m128i odd0sum(__m128i u0, __m128i u1sum)
348{
349 /* Mask out even bytes, extend uint8_t to uint16_t by filling in zero bytes,
350 * horizontally add the values */
351 const __m128i mask = mm_set_epu8(0x80, 0x0F, 0x80, 0x0D, 0x80, 0x0B, 0x80, 0x09, 0x80, 0x07,
352 0x80, 0x05, 0x80, 0x03, 0x80, 0x01);
353 const __m128i u0odd = _mm_shuffle_epi8(u0, mask);
354 return _mm_adds_epi16(u1sum, u0odd);
355}
356
357static inline __m128i calcavg(__m128i u0even, __m128i sum)
358{
359 const __m128i u4zero = _mm_slli_epi16(u0even, 2);
360 const __m128i uavg = _mm_sub_epi16(u4zero, sum);
361 const __m128i zero = _mm_set1_epi8(0);
362 const __m128i savg = _mm_packus_epi16(uavg, zero);
363 const __m128i smask = mm_set_epu8(0x80, 0x07, 0x80, 0x06, 0x80, 0x05, 0x80, 0x04, 0x80, 0x03,
364 0x80, 0x02, 0x80, 0x01, 0x80, 0x00);
365 return _mm_shuffle_epi8(savg, smask);
366}
367
368static inline __m128i diffmask(__m128i avg, __m128i u0even)
369{
370 /* Check for values >= 30 to apply the avg value to
371 * use int16 for calculations to avoid issues with signed 8bit integers
372 */
373 const __m128i diff = _mm_subs_epi16(u0even, avg);
374 const __m128i absdiff = _mm_abs_epi16(diff);
375 const __m128i val30 = _mm_set1_epi16(30);
376 return _mm_cmplt_epi16(absdiff, val30);
377}
378
379static inline void sse41_filter(__m128i pU[2])
380{
381 const __m128i u1sum = odd1sum(pU[1]);
382 const __m128i sum = odd0sum(pU[0], u1sum);
383
384 /* Mask out the odd bytes. We don“t need to do anything to make the uint8_t to uint16_t */
385 const __m128i emask = mm_set_epu8(0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
386 0x00, 0xff, 0x00, 0xff, 0x00, 0xff);
387 const __m128i u0even = _mm_and_si128(pU[0], emask);
388 const __m128i avg = calcavg(u0even, sum);
389 const __m128i umask = diffmask(avg, u0even);
390
391 const __m128i u0orig = _mm_and_si128(u0even, umask);
392 const __m128i u0avg = _mm_andnot_si128(umask, avg);
393 const __m128i evenresult = _mm_or_si128(u0orig, u0avg);
394 const __m128i omask = mm_set_epu8(0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
395 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00);
396 const __m128i u0odd = _mm_and_si128(pU[0], omask);
397 const __m128i result = _mm_or_si128(evenresult, u0odd);
398 pU[0] = result;
399}
400
401static inline void sse41_BGRX_fillRGB(BYTE* WINPR_RESTRICT pRGB[2], const __m128i pY[2],
402 __m128i pU[2], __m128i pV[2])
403{
404 WINPR_ASSERT(pRGB);
405 WINPR_ASSERT(pY);
406 WINPR_ASSERT(pU);
407 WINPR_ASSERT(pV);
408
409 sse41_filter(pU);
410 sse41_filter(pV);
411
412 for (size_t i = 0; i < 2; i++)
413 {
414 sse41_BGRX_fillRGB_pixel(pRGB[i], pY[i], pU[i], pV[i]);
415 }
416}
417
418static inline pstatus_t sse41_YUV444ToRGB_8u_P3AC4R_BGRX_DOUBLE_ROW(
419 BYTE* WINPR_RESTRICT pDst[2], const BYTE* WINPR_RESTRICT YData[2],
420 const BYTE* WINPR_RESTRICT UData[2], const BYTE* WINPR_RESTRICT VData[2], UINT32 nWidth)
421{
422 WINPR_ASSERT((nWidth % 2) == 0);
423 const UINT32 pad = nWidth % 16;
424
425 size_t x = 0;
426 for (; x < nWidth - pad; x += 16)
427 {
428 const __m128i Y[] = { LOAD_SI128(&YData[0][x]), LOAD_SI128(&YData[1][x]) };
429 __m128i U[] = { LOAD_SI128(&UData[0][x]), LOAD_SI128(&UData[1][x]) };
430 __m128i V[] = { LOAD_SI128(&VData[0][x]), LOAD_SI128(&VData[1][x]) };
431
432 BYTE* dstp[] = { &pDst[0][x * 4], &pDst[1][x * 4] };
433 sse41_BGRX_fillRGB(dstp, Y, U, V);
434 }
435
436 for (; x < nWidth; x += 2)
437 {
438 BGRX_fillRGB(x, pDst, YData, UData, VData, TRUE);
439 }
440
441 return PRIMITIVES_SUCCESS;
442}
443
444static inline void BGRX_fillRGB_single(size_t offset, BYTE* WINPR_RESTRICT pRGB,
445 const BYTE* WINPR_RESTRICT pY, const BYTE* WINPR_RESTRICT pU,
446 const BYTE* WINPR_RESTRICT pV, WINPR_ATTR_UNUSED BOOL filter)
447{
448 WINPR_ASSERT(pRGB);
449 WINPR_ASSERT(pY);
450 WINPR_ASSERT(pU);
451 WINPR_ASSERT(pV);
452
453 const UINT32 bpp = 4;
454
455 for (size_t j = 0; j < 2; j++)
456 {
457 const BYTE Y = pY[offset + j];
458 BYTE U = pU[offset + j];
459 BYTE V = pV[offset + j];
460
461 writeYUVPixel(&pRGB[(j + offset) * bpp], PIXEL_FORMAT_BGRX32, Y, U, V, writePixelBGRX);
462 }
463}
464
465static inline pstatus_t sse41_YUV444ToRGB_8u_P3AC4R_BGRX_SINGLE_ROW(
466 BYTE* WINPR_RESTRICT pDst, const BYTE* WINPR_RESTRICT YData, const BYTE* WINPR_RESTRICT UData,
467 const BYTE* WINPR_RESTRICT VData, UINT32 nWidth)
468{
469 WINPR_ASSERT((nWidth % 2) == 0);
470
471 for (size_t x = 0; x < nWidth; x += 2)
472 {
473 BGRX_fillRGB_single(x, pDst, YData, UData, VData, TRUE);
474 }
475
476 return PRIMITIVES_SUCCESS;
477}
478
479static inline pstatus_t sse41_YUV444ToRGB_8u_P3AC4R_BGRX(const BYTE* WINPR_RESTRICT pSrc[],
480 const UINT32 srcStep[],
481 BYTE* WINPR_RESTRICT pDst, UINT32 dstStep,
482 const prim_size_t* WINPR_RESTRICT roi)
483{
484 const UINT32 nWidth = roi->width;
485 const UINT32 nHeight = roi->height;
486
487 size_t y = 0;
488 for (; y < nHeight - nHeight % 2; y += 2)
489 {
490 BYTE* dst[] = { (pDst + dstStep * y), (pDst + dstStep * (y + 1)) };
491 const BYTE* YData[] = { pSrc[0] + y * srcStep[0], pSrc[0] + (y + 1) * srcStep[0] };
492 const BYTE* UData[] = { pSrc[1] + y * srcStep[1], pSrc[1] + (y + 1) * srcStep[1] };
493 const BYTE* VData[] = { pSrc[2] + y * srcStep[2], pSrc[2] + (y + 1) * srcStep[2] };
494
495 const pstatus_t rc =
496 sse41_YUV444ToRGB_8u_P3AC4R_BGRX_DOUBLE_ROW(dst, YData, UData, VData, nWidth);
497 if (rc != PRIMITIVES_SUCCESS)
498 return rc;
499 }
500 for (; y < nHeight; y++)
501 {
502 BYTE* dst = (pDst + dstStep * y);
503 const BYTE* YData = pSrc[0] + y * srcStep[0];
504 const BYTE* UData = pSrc[1] + y * srcStep[1];
505 const BYTE* VData = pSrc[2] + y * srcStep[2];
506 const pstatus_t rc =
507 sse41_YUV444ToRGB_8u_P3AC4R_BGRX_SINGLE_ROW(dst, YData, UData, VData, nWidth);
508 if (rc != PRIMITIVES_SUCCESS)
509 return rc;
510 }
511
512 return PRIMITIVES_SUCCESS;
513}
514
515static pstatus_t sse41_YUV444ToRGB_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc[],
516 const UINT32 srcStep[], BYTE* WINPR_RESTRICT pDst,
517 UINT32 dstStep, UINT32 DstFormat,
518 const prim_size_t* WINPR_RESTRICT roi)
519{
520 switch (DstFormat)
521 {
522 case PIXEL_FORMAT_BGRX32:
523 case PIXEL_FORMAT_BGRA32:
524 return sse41_YUV444ToRGB_8u_P3AC4R_BGRX(pSrc, srcStep, pDst, dstStep, roi);
525
526 default:
527 return generic->YUV444ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi);
528 }
529}
530
531/****************************************************************************/
532/* sse41 RGB -> YUV420 conversion **/
533/****************************************************************************/
534
556#define BGRX_Y_FACTORS _mm_set_epi8(0, 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9)
557#define BGRX_U_FACTORS \
558 _mm_set_epi8(0, -29, -99, 127, 0, -29, -99, 127, 0, -29, -99, 127, 0, -29, -99, 127)
559#define BGRX_V_FACTORS \
560 _mm_set_epi8(0, 127, -116, -12, 0, 127, -116, -12, 0, 127, -116, -12, 0, 127, -116, -12)
561#define CONST128_FACTORS _mm_set1_epi8(-128)
562
563#define Y_SHIFT 7
564#define U_SHIFT 8
565#define V_SHIFT 8
566
567/*
568TODO:
569RGB[AX] can simply be supported using the following factors. And instead of loading the
570globals directly the functions below could be passed pointers to the correct vectors
571depending on the source picture format.
572
573PRIM_ALIGN_128 static const BYTE rgbx_y_factors[] = {
574 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9, 0
575};
576PRIM_ALIGN_128 static const BYTE rgbx_u_factors[] = {
577 -15, -49, 64, 0, -15, -49, 64, 0, -15, -49, 64, 0, -15, -49, 64, 0
578};
579PRIM_ALIGN_128 static const BYTE rgbx_v_factors[] = {
580 64, -58, -6, 0, 64, -58, -6, 0, 64, -58, -6, 0, 64, -58, -6, 0
581};
582*/
583
584static inline void sse41_BGRX_TO_YUV(const BYTE* WINPR_RESTRICT pLine1, BYTE* WINPR_RESTRICT pYLine,
585 BYTE* WINPR_RESTRICT pULine, BYTE* WINPR_RESTRICT pVLine)
586{
587 const BYTE r1 = pLine1[2];
588 const BYTE g1 = pLine1[1];
589 const BYTE b1 = pLine1[0];
590
591 if (pYLine)
592 pYLine[0] = RGB2Y(r1, g1, b1);
593 if (pULine)
594 pULine[0] = RGB2U(r1, g1, b1);
595 if (pVLine)
596 pVLine[0] = RGB2V(r1, g1, b1);
597}
598
599/* compute the luma (Y) component from a single rgb source line */
600
601static inline void sse41_RGBToYUV420_BGRX_Y(const BYTE* WINPR_RESTRICT src, BYTE* dst, UINT32 width)
602{
603 const __m128i y_factors = BGRX_Y_FACTORS;
604 const __m128i* argb = WINPR_PACKED_ALIGN_CAST(const __m128i*, src);
605 __m128i* ydst = WINPR_PACKED_ALIGN_CAST(__m128i*, dst);
606
607 UINT32 x = 0;
608
609 for (; x < width - width % 16; x += 16)
610 {
611 /* store 16 rgba pixels in 4 128 bit registers */
612 __m128i x0 = LOAD_SI128(argb++); // 1st 4 pixels
613 {
614 x0 = _mm_maddubs_epi16(x0, y_factors);
615
616 __m128i x1 = LOAD_SI128(argb++); // 2nd 4 pixels
617 x1 = _mm_maddubs_epi16(x1, y_factors);
618 x0 = _mm_hadds_epi16(x0, x1);
619 x0 = _mm_srli_epi16(x0, Y_SHIFT);
620 }
621
622 __m128i x2 = LOAD_SI128(argb++); // 3rd 4 pixels
623 {
624 x2 = _mm_maddubs_epi16(x2, y_factors);
625
626 __m128i x3 = LOAD_SI128(argb++); // 4th 4 pixels
627 x3 = _mm_maddubs_epi16(x3, y_factors);
628 x2 = _mm_hadds_epi16(x2, x3);
629 x2 = _mm_srli_epi16(x2, Y_SHIFT);
630 }
631
632 x0 = _mm_packus_epi16(x0, x2);
633 /* save to y plane */
634 STORE_SI128(ydst++, x0);
635 }
636
637 for (; x < width; x++)
638 {
639 sse41_BGRX_TO_YUV(&src[4ULL * x], &dst[x], nullptr, nullptr);
640 }
641}
642
643/* compute the chrominance (UV) components from two rgb source lines */
644
645static inline void sse41_RGBToYUV420_BGRX_UV(const BYTE* WINPR_RESTRICT src1,
646 const BYTE* WINPR_RESTRICT src2,
647 BYTE* WINPR_RESTRICT dst1, BYTE* WINPR_RESTRICT dst2,
648 UINT32 width)
649{
650 const __m128i u_factors = BGRX_U_FACTORS;
651 const __m128i v_factors = BGRX_V_FACTORS;
652 const __m128i vector128 = CONST128_FACTORS;
653
654 size_t x = 0;
655
656 for (; x < width - width % 16; x += 16)
657 {
658 const __m128i* rgb1 = WINPR_PACKED_ALIGN_CAST(const __m128i*, &src1[4ULL * x]);
659 const __m128i* rgb2 = WINPR_PACKED_ALIGN_CAST(const __m128i*, &src2[4ULL * x]);
660 __m64* udst = WINPR_PACKED_ALIGN_CAST(__m64*, &dst1[x / 2]);
661 __m64* vdst = WINPR_PACKED_ALIGN_CAST(__m64*, &dst2[x / 2]);
662
663 /* subsample 16x2 pixels into 16x1 pixels */
664 __m128i x0 = LOAD_SI128(&rgb1[0]);
665 __m128i x4 = LOAD_SI128(&rgb2[0]);
666 x0 = _mm_avg_epu8(x0, x4);
667
668 __m128i x1 = LOAD_SI128(&rgb1[1]);
669 x4 = LOAD_SI128(&rgb2[1]);
670 x1 = _mm_avg_epu8(x1, x4);
671
672 __m128i x2 = LOAD_SI128(&rgb1[2]);
673 x4 = LOAD_SI128(&rgb2[2]);
674 x2 = _mm_avg_epu8(x2, x4);
675
676 __m128i x3 = LOAD_SI128(&rgb1[3]);
677 x4 = LOAD_SI128(&rgb2[3]);
678 x3 = _mm_avg_epu8(x3, x4);
679
680 /* subsample these 16x1 pixels into 8x1 pixels */
686 x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0x88));
687 x0 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0xdd));
688 x0 = _mm_avg_epu8(x0, x4);
689 x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0x88));
690 x1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0xdd));
691 x1 = _mm_avg_epu8(x1, x4);
692 /* multiplications and subtotals */
693 x2 = _mm_maddubs_epi16(x0, u_factors);
694 x3 = _mm_maddubs_epi16(x1, u_factors);
695 x4 = _mm_maddubs_epi16(x0, v_factors);
696 __m128i x5 = _mm_maddubs_epi16(x1, v_factors);
697 /* the total sums */
698 x0 = _mm_hadd_epi16(x2, x3);
699 x1 = _mm_hadd_epi16(x4, x5);
700 /* shift the results */
701 x0 = _mm_srai_epi16(x0, U_SHIFT);
702 x1 = _mm_srai_epi16(x1, V_SHIFT);
703 /* pack the 16 words into bytes */
704 x0 = _mm_packs_epi16(x0, x1);
705 /* add 128 */
706 x0 = _mm_sub_epi8(x0, vector128);
707 /* the lower 8 bytes go to the u plane */
708 _mm_storel_pi(udst, _mm_castsi128_ps(x0));
709 /* the upper 8 bytes go to the v plane */
710 _mm_storeh_pi(vdst, _mm_castsi128_ps(x0));
711 }
712
713 for (; x < width - width % 2; x += 2)
714 {
715 BYTE u[4] = WINPR_C_ARRAY_INIT;
716 BYTE v[4] = WINPR_C_ARRAY_INIT;
717 sse41_BGRX_TO_YUV(&src1[4ULL * x], nullptr, &u[0], &v[0]);
718 sse41_BGRX_TO_YUV(&src1[4ULL * (1ULL + x)], nullptr, &u[1], &v[1]);
719 sse41_BGRX_TO_YUV(&src2[4ULL * x], nullptr, &u[2], &v[2]);
720 sse41_BGRX_TO_YUV(&src2[4ULL * (1ULL + x)], nullptr, &u[3], &v[3]);
721 const INT16 u4 = WINPR_ASSERTING_INT_CAST(INT16, (INT16)u[0] + u[1] + u[2] + u[3]);
722 const INT16 uu = WINPR_ASSERTING_INT_CAST(INT16, u4 / 4);
723 const BYTE u8 = CLIP(uu);
724 dst1[x / 2] = u8;
725
726 const INT16 v4 = WINPR_ASSERTING_INT_CAST(INT16, (INT16)v[0] + v[1] + v[2] + v[3]);
727 const INT16 vu = WINPR_ASSERTING_INT_CAST(INT16, v4 / 4);
728 const BYTE v8 = CLIP(vu);
729 dst2[x / 2] = v8;
730 }
731}
732
733static pstatus_t sse41_RGBToYUV420_BGRX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
734 BYTE* WINPR_RESTRICT pDst[], const UINT32 dstStep[],
735 const prim_size_t* WINPR_RESTRICT roi)
736{
737 if (roi->height < 1 || roi->width < 1)
738 {
739 return !PRIMITIVES_SUCCESS;
740 }
741
742 size_t y = 0;
743 for (; y < roi->height - roi->height % 2; y += 2)
744 {
745 const BYTE* line1 = &pSrc[y * srcStep];
746 const BYTE* line2 = &pSrc[(1ULL + y) * srcStep];
747 BYTE* ydst1 = &pDst[0][y * dstStep[0]];
748 BYTE* ydst2 = &pDst[0][(1ULL + y) * dstStep[0]];
749 BYTE* udst = &pDst[1][y / 2 * dstStep[1]];
750 BYTE* vdst = &pDst[2][y / 2 * dstStep[2]];
751
752 sse41_RGBToYUV420_BGRX_UV(line1, line2, udst, vdst, roi->width);
753 sse41_RGBToYUV420_BGRX_Y(line1, ydst1, roi->width);
754 sse41_RGBToYUV420_BGRX_Y(line2, ydst2, roi->width);
755 }
756
757 for (; y < roi->height; y++)
758 {
759 const BYTE* line = &pSrc[y * srcStep];
760 BYTE* ydst = &pDst[0][1ULL * y * dstStep[0]];
761 sse41_RGBToYUV420_BGRX_Y(line, ydst, roi->width);
762 }
763
764 return PRIMITIVES_SUCCESS;
765}
766
767static pstatus_t sse41_RGBToYUV420(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
768 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[],
769 const UINT32 dstStep[], const prim_size_t* WINPR_RESTRICT roi)
770{
771 switch (srcFormat)
772 {
773 case PIXEL_FORMAT_BGRX32:
774 case PIXEL_FORMAT_BGRA32:
775 return sse41_RGBToYUV420_BGRX(pSrc, srcStep, pDst, dstStep, roi);
776
777 default:
778 return generic->RGBToYUV420_8u_P3AC4R(pSrc, srcFormat, srcStep, pDst, dstStep, roi);
779 }
780}
781
782/****************************************************************************/
783/* sse41 RGB -> AVC444-YUV conversion **/
784/****************************************************************************/
785
786static inline void sse41_RGBToAVC444YUV_BGRX_DOUBLE_ROW(
787 const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd,
788 BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
789 BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
790 BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
791{
792 const __m128i* argbEven = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcEven);
793 const __m128i* argbOdd = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcOdd);
794 const __m128i y_factors = BGRX_Y_FACTORS;
795 const __m128i u_factors = BGRX_U_FACTORS;
796 const __m128i v_factors = BGRX_V_FACTORS;
797 const __m128i vector128 = CONST128_FACTORS;
798
799 UINT32 x = 0;
800 for (; x < width - width % 16; x += 16)
801 {
802 /* store 16 rgba pixels in 4 128 bit registers */
803 const __m128i xe1 = LOAD_SI128(argbEven++); // 1st 4 pixels
804 const __m128i xe2 = LOAD_SI128(argbEven++); // 2nd 4 pixels
805 const __m128i xe3 = LOAD_SI128(argbEven++); // 3rd 4 pixels
806 const __m128i xe4 = LOAD_SI128(argbEven++); // 4th 4 pixels
807 const __m128i xo1 = LOAD_SI128(argbOdd++); // 1st 4 pixels
808 const __m128i xo2 = LOAD_SI128(argbOdd++); // 2nd 4 pixels
809 const __m128i xo3 = LOAD_SI128(argbOdd++); // 3rd 4 pixels
810 const __m128i xo4 = LOAD_SI128(argbOdd++); // 4th 4 pixels
811 {
812 /* Y: multiplications with subtotals and horizontal sums */
813 const __m128i ye1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, y_factors),
814 _mm_maddubs_epi16(xe2, y_factors)),
815 Y_SHIFT);
816 const __m128i ye2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, y_factors),
817 _mm_maddubs_epi16(xe4, y_factors)),
818 Y_SHIFT);
819 const __m128i ye = _mm_packus_epi16(ye1, ye2);
820 const __m128i yo1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, y_factors),
821 _mm_maddubs_epi16(xo2, y_factors)),
822 Y_SHIFT);
823 const __m128i yo2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, y_factors),
824 _mm_maddubs_epi16(xo4, y_factors)),
825 Y_SHIFT);
826 const __m128i yo = _mm_packus_epi16(yo1, yo2);
827 /* store y [b1] */
828 STORE_SI128(b1Even, ye);
829 b1Even += 16;
830
831 if (b1Odd)
832 {
833 STORE_SI128(b1Odd, yo);
834 b1Odd += 16;
835 }
836 }
837 {
838 /* We have now
839 * 16 even U values in ue
840 * 16 odd U values in uo
841 *
842 * We need to split these according to
843 * 3.3.8.3.2 YUV420p Stream Combination for YUV444 mode */
844 __m128i ue;
845 __m128i uo = WINPR_C_ARRAY_INIT;
846 {
847 const __m128i ue1 =
848 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, u_factors),
849 _mm_maddubs_epi16(xe2, u_factors)),
850 U_SHIFT);
851 const __m128i ue2 =
852 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, u_factors),
853 _mm_maddubs_epi16(xe4, u_factors)),
854 U_SHIFT);
855 ue = _mm_sub_epi8(_mm_packs_epi16(ue1, ue2), vector128);
856 }
857
858 if (b1Odd)
859 {
860 const __m128i uo1 =
861 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, u_factors),
862 _mm_maddubs_epi16(xo2, u_factors)),
863 U_SHIFT);
864 const __m128i uo2 =
865 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, u_factors),
866 _mm_maddubs_epi16(xo4, u_factors)),
867 U_SHIFT);
868 uo = _mm_sub_epi8(_mm_packs_epi16(uo1, uo2), vector128);
869 }
870
871 /* Now we need the following storage distribution:
872 * 2x 2y -> b2
873 * x 2y+1 -> b4
874 * 2x+1 2y -> b6 */
875 if (b1Odd) /* b2 */
876 {
877 const __m128i ueh = _mm_unpackhi_epi8(ue, _mm_setzero_si128());
878 const __m128i uoh = _mm_unpackhi_epi8(uo, _mm_setzero_si128());
879 const __m128i hi = _mm_add_epi16(ueh, uoh);
880 const __m128i uel = _mm_unpacklo_epi8(ue, _mm_setzero_si128());
881 const __m128i uol = _mm_unpacklo_epi8(uo, _mm_setzero_si128());
882 const __m128i lo = _mm_add_epi16(uel, uol);
883 const __m128i added = _mm_hadd_epi16(lo, hi);
884 const __m128i avg16 = _mm_srai_epi16(added, 2);
885 const __m128i avg = _mm_packus_epi16(avg16, avg16);
886 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b2), avg);
887 }
888 else
889 {
890 const __m128i mask =
891 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
892 (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
893 const __m128i ud = _mm_shuffle_epi8(ue, mask);
894 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b2), ud);
895 }
896
897 b2 += 8;
898
899 if (b1Odd) /* b4 */
900 {
901 STORE_SI128(b4, uo);
902 b4 += 16;
903 }
904
905 {
906 /* b6 */
907 const __m128i mask =
908 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
909 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
910 const __m128i ude = _mm_shuffle_epi8(ue, mask);
911 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b6), ude);
912 b6 += 8;
913 }
914 }
915 {
916 /* We have now
917 * 16 even V values in ue
918 * 16 odd V values in uo
919 *
920 * We need to split these according to
921 * 3.3.8.3.2 YUV420p Stream Combination for YUV444 mode */
922 __m128i ve;
923 __m128i vo = WINPR_C_ARRAY_INIT;
924 {
925 const __m128i ve1 =
926 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, v_factors),
927 _mm_maddubs_epi16(xe2, v_factors)),
928 V_SHIFT);
929 const __m128i ve2 =
930 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, v_factors),
931 _mm_maddubs_epi16(xe4, v_factors)),
932 V_SHIFT);
933 ve = _mm_sub_epi8(_mm_packs_epi16(ve1, ve2), vector128);
934 }
935
936 if (b1Odd)
937 {
938 const __m128i vo1 =
939 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, v_factors),
940 _mm_maddubs_epi16(xo2, v_factors)),
941 V_SHIFT);
942 const __m128i vo2 =
943 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, v_factors),
944 _mm_maddubs_epi16(xo4, v_factors)),
945 V_SHIFT);
946 vo = _mm_sub_epi8(_mm_packs_epi16(vo1, vo2), vector128);
947 }
948
949 /* Now we need the following storage distribution:
950 * 2x 2y -> b3
951 * x 2y+1 -> b5
952 * 2x+1 2y -> b7 */
953 if (b1Odd) /* b3 */
954 {
955 const __m128i veh = _mm_unpackhi_epi8(ve, _mm_setzero_si128());
956 const __m128i voh = _mm_unpackhi_epi8(vo, _mm_setzero_si128());
957 const __m128i hi = _mm_add_epi16(veh, voh);
958 const __m128i vel = _mm_unpacklo_epi8(ve, _mm_setzero_si128());
959 const __m128i vol = _mm_unpacklo_epi8(vo, _mm_setzero_si128());
960 const __m128i lo = _mm_add_epi16(vel, vol);
961 const __m128i added = _mm_hadd_epi16(lo, hi);
962 const __m128i avg16 = _mm_srai_epi16(added, 2);
963 const __m128i avg = _mm_packus_epi16(avg16, avg16);
964 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b3), avg);
965 }
966 else
967 {
968 const __m128i mask =
969 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
970 (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
971 const __m128i vd = _mm_shuffle_epi8(ve, mask);
972 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b3), vd);
973 }
974
975 b3 += 8;
976
977 if (b1Odd) /* b5 */
978 {
979 STORE_SI128(b5, vo);
980 b5 += 16;
981 }
982
983 {
984 /* b7 */
985 const __m128i mask =
986 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
987 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
988 const __m128i vde = _mm_shuffle_epi8(ve, mask);
989 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b7), vde);
990 b7 += 8;
991 }
992 }
993 }
994
995 general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(x, srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4, b5, b6,
996 b7, width);
997}
998
999static pstatus_t sse41_RGBToAVC444YUV_BGRX(const BYTE* WINPR_RESTRICT pSrc,
1000 WINPR_ATTR_UNUSED UINT32 srcFormat, UINT32 srcStep,
1001 BYTE* WINPR_RESTRICT pDst1[], const UINT32 dst1Step[],
1002 BYTE* WINPR_RESTRICT pDst2[], const UINT32 dst2Step[],
1003 const prim_size_t* WINPR_RESTRICT roi)
1004{
1005 if (roi->height < 1 || roi->width < 1)
1006 return !PRIMITIVES_SUCCESS;
1007
1008 size_t y = 0;
1009 for (; y < roi->height - roi->height % 2; y += 2)
1010 {
1011 const BYTE* srcEven = pSrc + y * srcStep;
1012 const BYTE* srcOdd = pSrc + (y + 1) * srcStep;
1013 const size_t i = y >> 1;
1014 const size_t n = (i & (size_t)~7) + i;
1015 BYTE* b1Even = pDst1[0] + y * dst1Step[0];
1016 BYTE* b1Odd = (b1Even + dst1Step[0]);
1017 BYTE* b2 = pDst1[1] + (y / 2) * dst1Step[1];
1018 BYTE* b3 = pDst1[2] + (y / 2) * dst1Step[2];
1019 BYTE* b4 = pDst2[0] + 1ULL * dst2Step[0] * n;
1020 BYTE* b5 = b4 + 8ULL * dst2Step[0];
1021 BYTE* b6 = pDst2[1] + (y / 2) * dst2Step[1];
1022 BYTE* b7 = pDst2[2] + (y / 2) * dst2Step[2];
1023 sse41_RGBToAVC444YUV_BGRX_DOUBLE_ROW(srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4, b5, b6, b7,
1024 roi->width);
1025 }
1026
1027 for (; y < roi->height; y++)
1028 {
1029 const BYTE* srcEven = pSrc + y * srcStep;
1030 BYTE* b1Even = pDst1[0] + y * dst1Step[0];
1031 BYTE* b2 = pDst1[1] + (y / 2) * dst1Step[1];
1032 BYTE* b3 = pDst1[2] + (y / 2) * dst1Step[2];
1033 BYTE* b6 = pDst2[1] + (y / 2) * dst2Step[1];
1034 BYTE* b7 = pDst2[2] + (y / 2) * dst2Step[2];
1035 general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(0, srcEven, nullptr, b1Even, nullptr, b2, b3,
1036 nullptr, nullptr, b6, b7, roi->width);
1037 }
1038
1039 return PRIMITIVES_SUCCESS;
1040}
1041
1042static pstatus_t sse41_RGBToAVC444YUV(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1043 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[],
1044 const UINT32 dst1Step[], BYTE* WINPR_RESTRICT pDst2[],
1045 const UINT32 dst2Step[],
1046 const prim_size_t* WINPR_RESTRICT roi)
1047{
1048 switch (srcFormat)
1049 {
1050 case PIXEL_FORMAT_BGRX32:
1051 case PIXEL_FORMAT_BGRA32:
1052 return sse41_RGBToAVC444YUV_BGRX(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1053 dst2Step, roi);
1054
1055 default:
1056 return generic->RGBToAVC444YUV(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1057 dst2Step, roi);
1058 }
1059}
1060
1061/* Mapping of arguments:
1062 *
1063 * b1 [even lines] -> yLumaDstEven
1064 * b1 [odd lines] -> yLumaDstOdd
1065 * b2 -> uLumaDst
1066 * b3 -> vLumaDst
1067 * b4 -> yChromaDst1
1068 * b5 -> yChromaDst2
1069 * b6 -> uChromaDst1
1070 * b7 -> uChromaDst2
1071 * b8 -> vChromaDst1
1072 * b9 -> vChromaDst2
1073 */
1074static inline void sse41_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
1075 const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd,
1076 BYTE* WINPR_RESTRICT yLumaDstEven, BYTE* WINPR_RESTRICT yLumaDstOdd,
1077 BYTE* WINPR_RESTRICT uLumaDst, BYTE* WINPR_RESTRICT vLumaDst,
1078 BYTE* WINPR_RESTRICT yEvenChromaDst1, BYTE* WINPR_RESTRICT yEvenChromaDst2,
1079 BYTE* WINPR_RESTRICT yOddChromaDst1, BYTE* WINPR_RESTRICT yOddChromaDst2,
1080 BYTE* WINPR_RESTRICT uChromaDst1, BYTE* WINPR_RESTRICT uChromaDst2,
1081 BYTE* WINPR_RESTRICT vChromaDst1, BYTE* WINPR_RESTRICT vChromaDst2, UINT32 width)
1082{
1083 const __m128i vector128 = CONST128_FACTORS;
1084 const __m128i* argbEven = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcEven);
1085 const __m128i* argbOdd = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcOdd);
1086
1087 UINT32 x = 0;
1088 for (; x < width - width % 16; x += 16)
1089 {
1090 /* store 16 rgba pixels in 4 128 bit registers
1091 * for even and odd rows.
1092 */
1093 const __m128i xe1 = LOAD_SI128(argbEven++); /* 1st 4 pixels */
1094 const __m128i xe2 = LOAD_SI128(argbEven++); /* 2nd 4 pixels */
1095 const __m128i xe3 = LOAD_SI128(argbEven++); /* 3rd 4 pixels */
1096 const __m128i xe4 = LOAD_SI128(argbEven++); /* 4th 4 pixels */
1097 const __m128i xo1 = LOAD_SI128(argbOdd++); /* 1st 4 pixels */
1098 const __m128i xo2 = LOAD_SI128(argbOdd++); /* 2nd 4 pixels */
1099 const __m128i xo3 = LOAD_SI128(argbOdd++); /* 3rd 4 pixels */
1100 const __m128i xo4 = LOAD_SI128(argbOdd++); /* 4th 4 pixels */
1101 {
1102 /* Y: multiplications with subtotals and horizontal sums */
1103 const __m128i y_factors = BGRX_Y_FACTORS;
1104 const __m128i ye1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, y_factors),
1105 _mm_maddubs_epi16(xe2, y_factors)),
1106 Y_SHIFT);
1107 const __m128i ye2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, y_factors),
1108 _mm_maddubs_epi16(xe4, y_factors)),
1109 Y_SHIFT);
1110 const __m128i ye = _mm_packus_epi16(ye1, ye2);
1111 /* store y [b1] */
1112 STORE_SI128(yLumaDstEven, ye);
1113 yLumaDstEven += 16;
1114 }
1115
1116 if (yLumaDstOdd)
1117 {
1118 const __m128i y_factors = BGRX_Y_FACTORS;
1119 const __m128i yo1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, y_factors),
1120 _mm_maddubs_epi16(xo2, y_factors)),
1121 Y_SHIFT);
1122 const __m128i yo2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, y_factors),
1123 _mm_maddubs_epi16(xo4, y_factors)),
1124 Y_SHIFT);
1125 const __m128i yo = _mm_packus_epi16(yo1, yo2);
1126 STORE_SI128(yLumaDstOdd, yo);
1127 yLumaDstOdd += 16;
1128 }
1129
1130 {
1131 /* We have now
1132 * 16 even U values in ue
1133 * 16 odd U values in uo
1134 *
1135 * We need to split these according to
1136 * 3.3.8.3.3 YUV420p Stream Combination for YUV444v2 mode */
1137 /* U: multiplications with subtotals and horizontal sums */
1138 __m128i ue;
1139 __m128i uo;
1140 __m128i uavg;
1141 {
1142 const __m128i u_factors = BGRX_U_FACTORS;
1143 const __m128i ue1 =
1144 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, u_factors),
1145 _mm_maddubs_epi16(xe2, u_factors)),
1146 U_SHIFT);
1147 const __m128i ue2 =
1148 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, u_factors),
1149 _mm_maddubs_epi16(xe4, u_factors)),
1150 U_SHIFT);
1151 const __m128i ueavg = _mm_hadd_epi16(ue1, ue2);
1152 ue = _mm_sub_epi8(_mm_packs_epi16(ue1, ue2), vector128);
1153 uavg = ueavg;
1154 }
1155 {
1156 const __m128i u_factors = BGRX_U_FACTORS;
1157 const __m128i uo1 =
1158 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, u_factors),
1159 _mm_maddubs_epi16(xo2, u_factors)),
1160 U_SHIFT);
1161 const __m128i uo2 =
1162 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, u_factors),
1163 _mm_maddubs_epi16(xo4, u_factors)),
1164 U_SHIFT);
1165 const __m128i uoavg = _mm_hadd_epi16(uo1, uo2);
1166 uo = _mm_sub_epi8(_mm_packs_epi16(uo1, uo2), vector128);
1167 uavg = _mm_add_epi16(uavg, uoavg);
1168 uavg = _mm_srai_epi16(uavg, 2);
1169 uavg = _mm_packs_epi16(uavg, uoavg);
1170 uavg = _mm_sub_epi8(uavg, vector128);
1171 }
1172 /* Now we need the following storage distribution:
1173 * 2x 2y -> uLumaDst
1174 * 2x+1 y -> yChromaDst1
1175 * 4x 2y+1 -> uChromaDst1
1176 * 4x+2 2y+1 -> vChromaDst1 */
1177 {
1178 const __m128i mask =
1179 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1180 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1181 const __m128i ude = _mm_shuffle_epi8(ue, mask);
1182 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yEvenChromaDst1), ude);
1183 yEvenChromaDst1 += 8;
1184 }
1185
1186 if (yLumaDstOdd)
1187 {
1188 const __m128i mask =
1189 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1190 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1191 const __m128i udo /* codespell:ignore udo */ = _mm_shuffle_epi8(uo, mask);
1192 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yOddChromaDst1),
1193 udo); // codespell:ignore udo
1194 yOddChromaDst1 += 8;
1195 }
1196
1197 if (yLumaDstOdd)
1198 {
1199 const __m128i mask =
1200 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1201 (char)0x80, (char)0x80, (char)0x80, 14, 10, 6, 2, 12, 8, 4, 0);
1202 const __m128i ud = _mm_shuffle_epi8(uo, mask);
1203 int* uDst1 = WINPR_PACKED_ALIGN_CAST(int*, uChromaDst1);
1204 int* vDst1 = WINPR_PACKED_ALIGN_CAST(int*, vChromaDst1);
1205 const int* src = (const int*)&ud;
1206 _mm_stream_si32(uDst1, src[0]);
1207 _mm_stream_si32(vDst1, src[1]);
1208 uChromaDst1 += 4;
1209 vChromaDst1 += 4;
1210 }
1211
1212 if (yLumaDstOdd)
1213 {
1214 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, uLumaDst), uavg);
1215 uLumaDst += 8;
1216 }
1217 else
1218 {
1219 const __m128i mask =
1220 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1221 (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
1222 const __m128i ud = _mm_shuffle_epi8(ue, mask);
1223 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, uLumaDst), ud);
1224 uLumaDst += 8;
1225 }
1226 }
1227
1228 {
1229 /* V: multiplications with subtotals and horizontal sums */
1230 __m128i ve;
1231 __m128i vo;
1232 __m128i vavg;
1233 {
1234 const __m128i v_factors = BGRX_V_FACTORS;
1235 const __m128i ve1 =
1236 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, v_factors),
1237 _mm_maddubs_epi16(xe2, v_factors)),
1238 V_SHIFT);
1239 const __m128i ve2 =
1240 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, v_factors),
1241 _mm_maddubs_epi16(xe4, v_factors)),
1242 V_SHIFT);
1243 const __m128i veavg = _mm_hadd_epi16(ve1, ve2);
1244 ve = _mm_sub_epi8(_mm_packs_epi16(ve1, ve2), vector128);
1245 vavg = veavg;
1246 }
1247 {
1248 const __m128i v_factors = BGRX_V_FACTORS;
1249 const __m128i vo1 =
1250 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, v_factors),
1251 _mm_maddubs_epi16(xo2, v_factors)),
1252 V_SHIFT);
1253 const __m128i vo2 =
1254 _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, v_factors),
1255 _mm_maddubs_epi16(xo4, v_factors)),
1256 V_SHIFT);
1257 const __m128i voavg = _mm_hadd_epi16(vo1, vo2);
1258 vo = _mm_sub_epi8(_mm_packs_epi16(vo1, vo2), vector128);
1259 vavg = _mm_add_epi16(vavg, voavg);
1260 vavg = _mm_srai_epi16(vavg, 2);
1261 vavg = _mm_packs_epi16(vavg, voavg);
1262 vavg = _mm_sub_epi8(vavg, vector128);
1263 }
1264 /* Now we need the following storage distribution:
1265 * 2x 2y -> vLumaDst
1266 * 2x+1 y -> yChromaDst2
1267 * 4x 2y+1 -> uChromaDst2
1268 * 4x+2 2y+1 -> vChromaDst2 */
1269 {
1270 const __m128i mask =
1271 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1272 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1273 __m128i vde = _mm_shuffle_epi8(ve, mask);
1274 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yEvenChromaDst2), vde);
1275 yEvenChromaDst2 += 8;
1276 }
1277
1278 if (yLumaDstOdd)
1279 {
1280 const __m128i mask =
1281 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1282 (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1283 __m128i vdo = _mm_shuffle_epi8(vo, mask);
1284 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yOddChromaDst2), vdo);
1285 yOddChromaDst2 += 8;
1286 }
1287
1288 if (yLumaDstOdd)
1289 {
1290 const __m128i mask =
1291 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1292 (char)0x80, (char)0x80, (char)0x80, 14, 10, 6, 2, 12, 8, 4, 0);
1293 const __m128i vd = _mm_shuffle_epi8(vo, mask);
1294 int* uDst2 = WINPR_PACKED_ALIGN_CAST(int*, uChromaDst2);
1295 int* vDst2 = WINPR_PACKED_ALIGN_CAST(int*, vChromaDst2);
1296 const int* src = (const int*)&vd;
1297 _mm_stream_si32(uDst2, src[0]);
1298 _mm_stream_si32(vDst2, src[1]);
1299 uChromaDst2 += 4;
1300 vChromaDst2 += 4;
1301 }
1302
1303 if (yLumaDstOdd)
1304 {
1305 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, vLumaDst), vavg);
1306 vLumaDst += 8;
1307 }
1308 else
1309 {
1310 const __m128i mask =
1311 _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1312 (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
1313 __m128i vd = _mm_shuffle_epi8(ve, mask);
1314 _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, vLumaDst), vd);
1315 vLumaDst += 8;
1316 }
1317 }
1318 }
1319
1320 general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(x, srcEven, srcOdd, yLumaDstEven, yLumaDstOdd,
1321 uLumaDst, vLumaDst, yEvenChromaDst1, yEvenChromaDst2,
1322 yOddChromaDst1, yOddChromaDst2, uChromaDst1,
1323 uChromaDst2, vChromaDst1, vChromaDst2, width);
1324}
1325
1326static pstatus_t sse41_RGBToAVC444YUVv2_BGRX(const BYTE* WINPR_RESTRICT pSrc,
1327 WINPR_ATTR_UNUSED UINT32 srcFormat, UINT32 srcStep,
1328 BYTE* WINPR_RESTRICT pDst1[], const UINT32 dst1Step[],
1329 BYTE* WINPR_RESTRICT pDst2[], const UINT32 dst2Step[],
1330 const prim_size_t* WINPR_RESTRICT roi)
1331{
1332 if (roi->height < 1 || roi->width < 1)
1333 return !PRIMITIVES_SUCCESS;
1334
1335 size_t y = 0;
1336 for (; y < roi->height - roi->height % 2; y += 2)
1337 {
1338 const BYTE* srcEven = (pSrc + y * srcStep);
1339 const BYTE* srcOdd = (srcEven + srcStep);
1340 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
1341 BYTE* dstLumaYOdd = (dstLumaYEven + dst1Step[0]);
1342 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
1343 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
1344 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
1345 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
1346 BYTE* dstOddChromaY1 = dstEvenChromaY1 + dst2Step[0];
1347 BYTE* dstOddChromaY2 = dstEvenChromaY2 + dst2Step[0];
1348 BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
1349 BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
1350 BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
1351 BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
1352 sse41_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(srcEven, srcOdd, dstLumaYEven, dstLumaYOdd, dstLumaU,
1353 dstLumaV, dstEvenChromaY1, dstEvenChromaY2,
1354 dstOddChromaY1, dstOddChromaY2, dstChromaU1,
1355 dstChromaU2, dstChromaV1, dstChromaV2, roi->width);
1356 }
1357
1358 for (; y < roi->height; y++)
1359 {
1360 const BYTE* srcEven = (pSrc + y * srcStep);
1361 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
1362 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
1363 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
1364 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
1365 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
1366 BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
1367 BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
1368 BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
1369 BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
1370 general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(0, srcEven, nullptr, dstLumaYEven, nullptr,
1371 dstLumaU, dstLumaV, dstEvenChromaY1,
1372 dstEvenChromaY2, nullptr, nullptr, dstChromaU1,
1373 dstChromaU2, dstChromaV1, dstChromaV2, roi->width);
1374 }
1375
1376 return PRIMITIVES_SUCCESS;
1377}
1378
1379static pstatus_t sse41_RGBToAVC444YUVv2(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1380 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[],
1381 const UINT32 dst1Step[], BYTE* WINPR_RESTRICT pDst2[],
1382 const UINT32 dst2Step[],
1383 const prim_size_t* WINPR_RESTRICT roi)
1384{
1385 switch (srcFormat)
1386 {
1387 case PIXEL_FORMAT_BGRX32:
1388 case PIXEL_FORMAT_BGRA32:
1389 return sse41_RGBToAVC444YUVv2_BGRX(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1390 dst2Step, roi);
1391
1392 default:
1393 return generic->RGBToAVC444YUVv2(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1394 dst2Step, roi);
1395 }
1396}
1397
1398static pstatus_t sse41_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[], const UINT32 srcStep[],
1399 BYTE* WINPR_RESTRICT pDstRaw[], const UINT32 dstStep[],
1400 const RECTANGLE_16* WINPR_RESTRICT roi)
1401{
1402 const UINT32 nWidth = roi->right - roi->left;
1403 const UINT32 nHeight = roi->bottom - roi->top;
1404 const UINT32 halfWidth = (nWidth + 1) / 2;
1405 const UINT32 halfPad = halfWidth % 16;
1406 const UINT32 halfHeight = (nHeight + 1) / 2;
1407 const UINT32 oddY = 1;
1408 const UINT32 evenY = 0;
1409 const UINT32 oddX = 1;
1410 const UINT32 evenX = 0;
1411 const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
1412 pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
1413 pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
1414 BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
1415 pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
1416 pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
1417
1418 /* Y data is already here... */
1419 /* B1 */
1420 for (size_t y = 0; y < nHeight; y++)
1421 {
1422 const BYTE* Ym = pSrc[0] + y * srcStep[0];
1423 BYTE* pY = pDst[0] + y * dstStep[0];
1424 memcpy(pY, Ym, nWidth);
1425 }
1426
1427 /* The first half of U, V are already here part of this frame. */
1428 /* B2 and B3 */
1429 for (size_t y = 0; y < halfHeight; y++)
1430 {
1431 const size_t val2y = (2 * y + evenY);
1432 const size_t val2y1 = val2y + oddY;
1433 const BYTE* Um = pSrc[1] + 1ULL * srcStep[1] * y;
1434 const BYTE* Vm = pSrc[2] + 1ULL * srcStep[2] * y;
1435 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * val2y;
1436 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * val2y;
1437 BYTE* pU1 = pDst[1] + 1ULL * dstStep[1] * val2y1;
1438 BYTE* pV1 = pDst[2] + 1ULL * dstStep[2] * val2y1;
1439
1440 size_t x = 0;
1441 for (; x < halfWidth - halfPad; x += 16)
1442 {
1443 const __m128i unpackHigh = _mm_set_epi8(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0);
1444 const __m128i unpackLow =
1445 _mm_set_epi8(15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8);
1446 {
1447 const __m128i u = LOAD_SI128(&Um[x]);
1448 const __m128i uHigh = _mm_shuffle_epi8(u, unpackHigh);
1449 const __m128i uLow = _mm_shuffle_epi8(u, unpackLow);
1450 STORE_SI128(&pU[2ULL * x], uHigh);
1451 STORE_SI128(&pU[2ULL * x + 16], uLow);
1452 STORE_SI128(&pU1[2ULL * x], uHigh);
1453 STORE_SI128(&pU1[2ULL * x + 16], uLow);
1454 }
1455 {
1456 const __m128i u = LOAD_SI128(&Vm[x]);
1457 const __m128i uHigh = _mm_shuffle_epi8(u, unpackHigh);
1458 const __m128i uLow = _mm_shuffle_epi8(u, unpackLow);
1459 STORE_SI128(&pV[2 * x], uHigh);
1460 STORE_SI128(&pV[2 * x + 16], uLow);
1461 STORE_SI128(&pV1[2 * x], uHigh);
1462 STORE_SI128(&pV1[2 * x + 16], uLow);
1463 }
1464 }
1465
1466 for (; x < halfWidth; x++)
1467 {
1468 const size_t val2x = 2 * x + evenX;
1469 const size_t val2x1 = val2x + oddX;
1470 pU[val2x] = Um[x];
1471 pV[val2x] = Vm[x];
1472 pU[val2x1] = Um[x];
1473 pV[val2x1] = Vm[x];
1474 pU1[val2x] = Um[x];
1475 pV1[val2x] = Vm[x];
1476 pU1[val2x1] = Um[x];
1477 pV1[val2x1] = Vm[x];
1478 }
1479 }
1480
1481 return PRIMITIVES_SUCCESS;
1482}
1483
1484static pstatus_t sse41_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
1485 const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDstRaw[3],
1486 const UINT32 dstStep[3],
1487 const RECTANGLE_16* WINPR_RESTRICT roi)
1488{
1489 const UINT32 mod = 16;
1490 UINT32 uY = 0;
1491 UINT32 vY = 0;
1492 const UINT32 nWidth = roi->right - roi->left;
1493 const UINT32 nHeight = roi->bottom - roi->top;
1494 const UINT32 halfWidth = (nWidth + 1) / 2;
1495 const UINT32 halfPad = halfWidth % 16;
1496 const UINT32 halfHeight = (nHeight + 1) / 2;
1497 const UINT32 oddY = 1;
1498 const UINT32 evenY = 0;
1499 const UINT32 oddX = 1;
1500 /* The auxiliary frame is aligned to multiples of 16x16.
1501 * We need the padded height for B4 and B5 conversion. */
1502 const UINT32 padHeight = nHeight + 16 - nHeight % 16;
1503 const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
1504 pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
1505 pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
1506 BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
1507 pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
1508 pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
1509 const __m128i zero = _mm_setzero_si128();
1510 const __m128i mask = _mm_set_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0,
1511 (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80);
1512
1513 /* The second half of U and V is a bit more tricky... */
1514 /* B4 and B5 */
1515 for (size_t y = 0; y < padHeight; y++)
1516 {
1517 const BYTE* Ya = pSrc[0] + 1ULL * srcStep[0] * y;
1518 BYTE* pX = nullptr;
1519
1520 if ((y) % mod < (mod + 1) / 2)
1521 {
1522 const UINT32 pos = (2 * uY++ + oddY);
1523
1524 if (pos >= nHeight)
1525 continue;
1526
1527 pX = pDst[1] + 1ULL * dstStep[1] * pos;
1528 }
1529 else
1530 {
1531 const UINT32 pos = (2 * vY++ + oddY);
1532
1533 if (pos >= nHeight)
1534 continue;
1535
1536 pX = pDst[2] + 1ULL * dstStep[2] * pos;
1537 }
1538
1539 if (y < nHeight)
1540 memcpy(pX, Ya, nWidth);
1541 }
1542
1543 /* B6 and B7 */
1544 for (size_t y = 0; y < halfHeight; y++)
1545 {
1546 const size_t val2y = (y * 2 + evenY);
1547 const BYTE* Ua = pSrc[1] + srcStep[1] * y;
1548 const BYTE* Va = pSrc[2] + srcStep[2] * y;
1549 BYTE* pU = pDst[1] + dstStep[1] * val2y;
1550 BYTE* pV = pDst[2] + dstStep[2] * val2y;
1551
1552 size_t x = 0;
1553 for (; x < halfWidth - halfPad; x += 16)
1554 {
1555 {
1556 const __m128i u = LOAD_SI128(&Ua[x]);
1557 const __m128i u2 = _mm_unpackhi_epi8(u, zero);
1558 const __m128i u1 = _mm_unpacklo_epi8(u, zero);
1559 _mm_maskmoveu_si128(u1, mask, (char*)&pU[2 * x]);
1560 _mm_maskmoveu_si128(u2, mask, (char*)&pU[2 * x + 16]);
1561 }
1562 {
1563 const __m128i u = LOAD_SI128(&Va[x]);
1564 const __m128i u2 = _mm_unpackhi_epi8(u, zero);
1565 const __m128i u1 = _mm_unpacklo_epi8(u, zero);
1566 _mm_maskmoveu_si128(u1, mask, (char*)&pV[2 * x]);
1567 _mm_maskmoveu_si128(u2, mask, (char*)&pV[2 * x + 16]);
1568 }
1569 }
1570
1571 for (; x < halfWidth; x++)
1572 {
1573 const size_t val2x1 = (x * 2ULL + oddX);
1574 pU[val2x1] = Ua[x];
1575 pV[val2x1] = Va[x];
1576 }
1577 }
1578
1579 return PRIMITIVES_SUCCESS;
1580}
1581
1582static pstatus_t sse41_ChromaV2ToYUV444(const BYTE* WINPR_RESTRICT pSrc[3], const UINT32 srcStep[3],
1583 UINT32 nTotalWidth, WINPR_ATTR_UNUSED UINT32 nTotalHeight,
1584 BYTE* WINPR_RESTRICT pDst[3], const UINT32 dstStep[3],
1585 const RECTANGLE_16* WINPR_RESTRICT roi)
1586{
1587 const UINT32 nWidth = roi->right - roi->left;
1588 const UINT32 nHeight = roi->bottom - roi->top;
1589 const UINT32 halfWidth = (nWidth + 1) / 2;
1590 const UINT32 halfPad = halfWidth % 16;
1591 const UINT32 halfHeight = (nHeight + 1) / 2;
1592 const UINT32 quaterWidth = (nWidth + 3) / 4;
1593 const UINT32 quaterPad = quaterWidth % 16;
1594 const __m128i zero = _mm_setzero_si128();
1595 const __m128i mask = _mm_set_epi8((char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0,
1596 (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0);
1597 const __m128i mask2 = _mm_set_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80,
1598 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80);
1599 const __m128i shuffle1 =
1600 _mm_set_epi8((char)0x80, 15, (char)0x80, 14, (char)0x80, 13, (char)0x80, 12, (char)0x80, 11,
1601 (char)0x80, 10, (char)0x80, 9, (char)0x80, 8);
1602 const __m128i shuffle2 =
1603 _mm_set_epi8((char)0x80, 7, (char)0x80, 6, (char)0x80, 5, (char)0x80, 4, (char)0x80, 3,
1604 (char)0x80, 2, (char)0x80, 1, (char)0x80, 0);
1605
1606 /* B4 and B5: odd UV values for width/2, height */
1607 for (size_t y = 0; y < nHeight; y++)
1608 {
1609 const size_t yTop = y + roi->top;
1610 const BYTE* pYaU = pSrc[0] + srcStep[0] * yTop + roi->left / 2;
1611 const BYTE* pYaV = pYaU + nTotalWidth / 2;
1612 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * yTop + roi->left;
1613 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * yTop + roi->left;
1614
1615 size_t x = 0;
1616 for (; x < halfWidth - halfPad; x += 16)
1617 {
1618 {
1619 const __m128i u = LOAD_SI128(&pYaU[x]);
1620 const __m128i u2 = _mm_unpackhi_epi8(zero, u);
1621 const __m128i u1 = _mm_unpacklo_epi8(zero, u);
1622 _mm_maskmoveu_si128(u1, mask, (char*)&pU[2 * x]);
1623 _mm_maskmoveu_si128(u2, mask, (char*)&pU[2 * x + 16]);
1624 }
1625 {
1626 const __m128i v = LOAD_SI128(&pYaV[x]);
1627 const __m128i v2 = _mm_unpackhi_epi8(zero, v);
1628 const __m128i v1 = _mm_unpacklo_epi8(zero, v);
1629 _mm_maskmoveu_si128(v1, mask, (char*)&pV[2 * x]);
1630 _mm_maskmoveu_si128(v2, mask, (char*)&pV[2 * x + 16]);
1631 }
1632 }
1633
1634 for (; x < halfWidth; x++)
1635 {
1636 const size_t odd = 2ULL * x + 1;
1637 pU[odd] = pYaU[x];
1638 pV[odd] = pYaV[x];
1639 }
1640 }
1641
1642 /* B6 - B9 */
1643 for (size_t y = 0; y < halfHeight; y++)
1644 {
1645 const BYTE* pUaU = pSrc[1] + srcStep[1] * (y + roi->top / 2) + roi->left / 4;
1646 const BYTE* pUaV = pUaU + nTotalWidth / 4;
1647 const BYTE* pVaU = pSrc[2] + srcStep[2] * (y + roi->top / 2) + roi->left / 4;
1648 const BYTE* pVaV = pVaU + nTotalWidth / 4;
1649 BYTE* pU = pDst[1] + dstStep[1] * (2 * y + 1 + roi->top) + roi->left;
1650 BYTE* pV = pDst[2] + dstStep[2] * (2 * y + 1 + roi->top) + roi->left;
1651
1652 UINT32 x = 0;
1653 for (; x < quaterWidth - quaterPad; x += 16)
1654 {
1655 {
1656 const __m128i uU = LOAD_SI128(&pUaU[x]);
1657 const __m128i uV = LOAD_SI128(&pVaU[x]);
1658 const __m128i uHigh = _mm_unpackhi_epi8(uU, uV);
1659 const __m128i uLow = _mm_unpacklo_epi8(uU, uV);
1660 const __m128i u1 = _mm_shuffle_epi8(uLow, shuffle2);
1661 const __m128i u2 = _mm_shuffle_epi8(uLow, shuffle1);
1662 const __m128i u3 = _mm_shuffle_epi8(uHigh, shuffle2);
1663 const __m128i u4 = _mm_shuffle_epi8(uHigh, shuffle1);
1664 _mm_maskmoveu_si128(u1, mask2, (char*)&pU[4 * x + 0]);
1665 _mm_maskmoveu_si128(u2, mask2, (char*)&pU[4 * x + 16]);
1666 _mm_maskmoveu_si128(u3, mask2, (char*)&pU[4 * x + 32]);
1667 _mm_maskmoveu_si128(u4, mask2, (char*)&pU[4 * x + 48]);
1668 }
1669 {
1670 const __m128i vU = LOAD_SI128(&pUaV[x]);
1671 const __m128i vV = LOAD_SI128(&pVaV[x]);
1672 const __m128i vHigh = _mm_unpackhi_epi8(vU, vV);
1673 const __m128i vLow = _mm_unpacklo_epi8(vU, vV);
1674 const __m128i v1 = _mm_shuffle_epi8(vLow, shuffle2);
1675 const __m128i v2 = _mm_shuffle_epi8(vLow, shuffle1);
1676 const __m128i v3 = _mm_shuffle_epi8(vHigh, shuffle2);
1677 const __m128i v4 = _mm_shuffle_epi8(vHigh, shuffle1);
1678 _mm_maskmoveu_si128(v1, mask2, (char*)&pV[4 * x + 0]);
1679 _mm_maskmoveu_si128(v2, mask2, (char*)&pV[4 * x + 16]);
1680 _mm_maskmoveu_si128(v3, mask2, (char*)&pV[4 * x + 32]);
1681 _mm_maskmoveu_si128(v4, mask2, (char*)&pV[4 * x + 48]);
1682 }
1683 }
1684
1685 for (; x < quaterWidth; x++)
1686 {
1687 pU[4 * x + 0] = pUaU[x];
1688 pV[4 * x + 0] = pUaV[x];
1689 pU[4 * x + 2] = pVaU[x];
1690 pV[4 * x + 2] = pVaV[x];
1691 }
1692 }
1693
1694 return PRIMITIVES_SUCCESS;
1695}
1696
1697static pstatus_t sse41_YUV420CombineToYUV444(avc444_frame_type type,
1698 const BYTE* WINPR_RESTRICT pSrc[3],
1699 const UINT32 srcStep[3], UINT32 nWidth, UINT32 nHeight,
1700 BYTE* WINPR_RESTRICT pDst[3], const UINT32 dstStep[3],
1701 const RECTANGLE_16* WINPR_RESTRICT roi)
1702{
1703 if (!pSrc || !pSrc[0] || !pSrc[1] || !pSrc[2])
1704 return -1;
1705
1706 if (!pDst || !pDst[0] || !pDst[1] || !pDst[2])
1707 return -1;
1708
1709 if (!roi)
1710 return -1;
1711
1712 switch (type)
1713 {
1714 case AVC444_LUMA:
1715 return sse41_LumaToYUV444(pSrc, srcStep, pDst, dstStep, roi);
1716
1717 case AVC444_CHROMAv1:
1718 return sse41_ChromaV1ToYUV444(pSrc, srcStep, pDst, dstStep, roi);
1719
1720 case AVC444_CHROMAv2:
1721 return sse41_ChromaV2ToYUV444(pSrc, srcStep, nWidth, nHeight, pDst, dstStep, roi);
1722
1723 default:
1724 return -1;
1725 }
1726}
1727#endif
1728
1729void primitives_init_YUV_sse41_int(primitives_t* WINPR_RESTRICT prims)
1730{
1731#if defined(SSE_AVX_INTRINSICS_ENABLED)
1732 generic = primitives_get_generic();
1733
1734 WLog_VRB(PRIM_TAG, "SSE3/sse41 optimizations");
1735 prims->RGBToYUV420_8u_P3AC4R = sse41_RGBToYUV420;
1736 prims->RGBToAVC444YUV = sse41_RGBToAVC444YUV;
1737 prims->RGBToAVC444YUVv2 = sse41_RGBToAVC444YUVv2;
1738 prims->YUV420ToRGB_8u_P3AC4R = sse41_YUV420ToRGB;
1739 prims->YUV444ToRGB_8u_P3AC4R = sse41_YUV444ToRGB_8u_P3AC4R;
1740 prims->YUV420CombineToYUV444 = sse41_YUV420CombineToYUV444;
1741#else
1742 WLog_VRB(PRIM_TAG, "undefined WITH_SIMD or sse41 intrinsics not available");
1743 WINPR_UNUSED(prims);
1744#endif
1745}