Coverage Report

Created: 2026-09-14 06:32

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