Coverage Report

Created: 2026-10-15 06:31

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
  const UINT32 pad = nWidth % 16;
423
424
0
  size_t x = 0;
425
0
  for (; x < nWidth - pad; x += 16)
426
0
  {
427
0
    const __m128i Y[] = { LOAD_SI128(&YData[0][x]), LOAD_SI128(&YData[1][x]) };
428
0
    __m128i U[] = { LOAD_SI128(&UData[0][x]), LOAD_SI128(&UData[1][x]) };
429
0
    __m128i V[] = { LOAD_SI128(&VData[0][x]), LOAD_SI128(&VData[1][x]) };
430
431
0
    BYTE* dstp[] = { &pDst[0][x * 4], &pDst[1][x * 4] };
432
0
    sse41_BGRX_fillRGB(dstp, Y, U, V);
433
0
  }
434
435
0
  for (; x < nWidth; x += 2)
436
0
  {
437
0
    BGRX_fillRGB(x, pDst, YData, UData, VData, TRUE);
438
0
  }
439
440
0
  return PRIMITIVES_SUCCESS;
441
0
}
442
443
static inline void BGRX_fillRGB_single(size_t offset, BYTE* WINPR_RESTRICT pRGB,
444
                                       const BYTE* WINPR_RESTRICT pY, const BYTE* WINPR_RESTRICT pU,
445
                                       const BYTE* WINPR_RESTRICT pV, WINPR_ATTR_UNUSED BOOL filter)
446
0
{
447
0
  WINPR_ASSERT(pRGB);
448
0
  WINPR_ASSERT(pY);
449
0
  WINPR_ASSERT(pU);
450
0
  WINPR_ASSERT(pV);
451
452
0
  const UINT32 bpp = 4;
453
454
0
  for (size_t j = 0; j < 2; j++)
455
0
  {
456
0
    const BYTE Y = pY[offset + j];
457
0
    BYTE U = pU[offset + j];
458
0
    BYTE V = pV[offset + j];
459
460
0
    writeYUVPixel(&pRGB[(j + offset) * bpp], PIXEL_FORMAT_BGRX32, Y, U, V, writePixelBGRX);
461
0
  }
462
0
}
463
464
static inline pstatus_t sse41_YUV444ToRGB_8u_P3AC4R_BGRX_SINGLE_ROW(
465
    BYTE* WINPR_RESTRICT pDst, const BYTE* WINPR_RESTRICT YData, const BYTE* WINPR_RESTRICT UData,
466
    const BYTE* WINPR_RESTRICT VData, UINT32 nWidth)
467
0
{
468
0
  for (size_t x = 0; x < nWidth; x += 2)
469
0
  {
470
0
    BGRX_fillRGB_single(x, pDst, YData, UData, VData, TRUE);
471
0
  }
472
473
0
  return PRIMITIVES_SUCCESS;
474
0
}
475
476
static inline pstatus_t sse41_YUV444ToRGB_8u_P3AC4R_BGRX(const BYTE* WINPR_RESTRICT pSrc[],
477
                                                         const UINT32 srcStep[],
478
                                                         BYTE* WINPR_RESTRICT pDst, UINT32 dstStep,
479
                                                         const prim_size_t* WINPR_RESTRICT roi)
480
0
{
481
0
  const UINT32 nWidth = roi->width;
482
0
  const UINT32 nHeight = roi->height;
483
484
0
  size_t y = 0;
485
0
  for (; y < nHeight - nHeight % 2; y += 2)
486
0
  {
487
0
    BYTE* dst[] = { (pDst + dstStep * y), (pDst + dstStep * (y + 1)) };
488
0
    const BYTE* YData[] = { pSrc[0] + y * srcStep[0], pSrc[0] + (y + 1) * srcStep[0] };
489
0
    const BYTE* UData[] = { pSrc[1] + y * srcStep[1], pSrc[1] + (y + 1) * srcStep[1] };
490
0
    const BYTE* VData[] = { pSrc[2] + y * srcStep[2], pSrc[2] + (y + 1) * srcStep[2] };
491
492
0
    const pstatus_t rc =
493
0
        sse41_YUV444ToRGB_8u_P3AC4R_BGRX_DOUBLE_ROW(dst, YData, UData, VData, nWidth);
494
0
    if (rc != PRIMITIVES_SUCCESS)
495
0
      return rc;
496
0
  }
497
0
  for (; y < nHeight; y++)
498
0
  {
499
0
    BYTE* dst = (pDst + dstStep * y);
500
0
    const BYTE* YData = pSrc[0] + y * srcStep[0];
501
0
    const BYTE* UData = pSrc[1] + y * srcStep[1];
502
0
    const BYTE* VData = pSrc[2] + y * srcStep[2];
503
0
    const pstatus_t rc =
504
0
        sse41_YUV444ToRGB_8u_P3AC4R_BGRX_SINGLE_ROW(dst, YData, UData, VData, nWidth);
505
0
    if (rc != PRIMITIVES_SUCCESS)
506
0
      return rc;
507
0
  }
508
509
0
  return PRIMITIVES_SUCCESS;
510
0
}
511
512
static pstatus_t sse41_YUV444ToRGB_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc[],
513
                                             const UINT32 srcStep[], BYTE* WINPR_RESTRICT pDst,
514
                                             UINT32 dstStep, UINT32 DstFormat,
515
                                             const prim_size_t* WINPR_RESTRICT roi)
516
0
{
517
0
  switch (DstFormat)
518
0
  {
519
0
    case PIXEL_FORMAT_BGRX32:
520
0
    case PIXEL_FORMAT_BGRA32:
521
0
      return sse41_YUV444ToRGB_8u_P3AC4R_BGRX(pSrc, srcStep, pDst, dstStep, roi);
522
523
0
    default:
524
0
      return generic->YUV444ToRGB_8u_P3AC4R(pSrc, srcStep, pDst, dstStep, DstFormat, roi);
525
0
  }
526
0
}
527
528
static inline void sse41_I444ToRGB_BGRX_ROW(BYTE* WINPR_RESTRICT pDst,
529
                                            const BYTE* WINPR_RESTRICT YData,
530
                                            const BYTE* WINPR_RESTRICT UData,
531
                                            const BYTE* WINPR_RESTRICT VData, UINT32 nWidth)
532
0
{
533
0
  const UINT32 pad = nWidth % 16;
534
535
0
  size_t x = 0;
536
0
  for (; x < nWidth - pad; x += 16)
537
0
  {
538
0
    const __m128i Y = LOAD_SI128(&YData[x]);
539
0
    const __m128i U = LOAD_SI128(&UData[x]);
540
0
    const __m128i V = LOAD_SI128(&VData[x]);
541
542
0
    sse41_BGRX_fillRGB_pixel(&pDst[x * 4], Y, U, V);
543
0
  }
544
545
0
  for (; x < nWidth; x++)
546
0
  {
547
0
    writeYUVPixel(&pDst[x * 4], PIXEL_FORMAT_BGRX32, YData[x], UData[x], VData[x],
548
0
                  writePixelBGRX);
549
0
  }
550
0
}
551
552
static pstatus_t sse41_I444ToRGB_8u(const BYTE* WINPR_RESTRICT pSrc[], const UINT32 srcStep[],
553
                                    BYTE* WINPR_RESTRICT pDst, UINT32 dstStep, UINT32 DstFormat,
554
                                    const prim_size_t* WINPR_RESTRICT roi)
555
0
{
556
0
  switch (DstFormat)
557
0
  {
558
0
    case PIXEL_FORMAT_BGRX32:
559
0
    case PIXEL_FORMAT_BGRA32:
560
0
      for (size_t y = 0; y < roi->height; y++)
561
0
      {
562
0
        BYTE* dst = pDst + dstStep * y;
563
0
        const BYTE* YData = pSrc[0] + y * srcStep[0];
564
0
        const BYTE* UData = pSrc[1] + y * srcStep[1];
565
0
        const BYTE* VData = pSrc[2] + y * srcStep[2];
566
567
0
        sse41_I444ToRGB_BGRX_ROW(dst, YData, UData, VData, roi->width);
568
0
      }
569
0
      return PRIMITIVES_SUCCESS;
570
571
0
    default:
572
0
      return generic->I444ToRGB_8u(pSrc, srcStep, pDst, dstStep, DstFormat, roi);
573
0
  }
574
0
}
575
576
/****************************************************************************/
577
/* sse41 RGB -> YUV420 conversion                                          **/
578
/****************************************************************************/
579
580
/**
581
 * Note (nfedera):
582
 * The used forward transformation factors from RGB to YUV are based on the
583
 * values specified in [Rec. ITU-R BT.709-6] Section 3:
584
 * http://www.itu.int/rec/R-REC-BT.709-6-201506-I/en
585
 *
586
 * Y =  0.21260 * R + 0.71520 * G + 0.07220 * B +   0;
587
 * U = -0.11457 * R - 0.38543 * G + 0.50000 * B + 128;
588
 * V =  0.50000 * R - 0.45415 * G - 0.04585 * B + 128;
589
 *
590
 * The most accurate integer arithmetic approximation when using 8-bit signed
591
 * integer factors with 16-bit signed integer intermediate results is:
592
 *
593
 * Y = ( ( 27 * R + 92 * G +  9 * B) >> 7 );
594
 * U = ( (-29 * R - 99 * G + 128 * B) >> 8 ) + 128;
595
 * V = ( ( 128 * R - 116 * G -  12 * B) >> 8 ) + 128;
596
 *
597
 * Due to signed 8bit range being [-128,127] the U and V constants of 128 are
598
 * rounded to 127
599
 */
600
601
0
#define BGRX_Y_FACTORS _mm_set_epi8(0, 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9, 0, 27, 92, 9)
602
#define BGRX_U_FACTORS \
603
0
  _mm_set_epi8(0, -29, -99, 127, 0, -29, -99, 127, 0, -29, -99, 127, 0, -29, -99, 127)
604
#define BGRX_V_FACTORS \
605
0
  _mm_set_epi8(0, 127, -116, -12, 0, 127, -116, -12, 0, 127, -116, -12, 0, 127, -116, -12)
606
0
#define CONST128_FACTORS _mm_set1_epi8(-128)
607
608
0
#define Y_SHIFT 7
609
0
#define U_SHIFT 8
610
0
#define V_SHIFT 8
611
612
/*
613
TODO:
614
RGB[AX] can simply be supported using the following factors. And instead of loading the
615
globals directly the functions below could be passed pointers to the correct vectors
616
depending on the source picture format.
617
618
PRIM_ALIGN_128 static const BYTE rgbx_y_factors[] = {
619
      27,  92,   9,   0,  27,  92,   9,   0,  27,  92,   9,   0,  27,  92,   9,   0
620
};
621
PRIM_ALIGN_128 static const BYTE rgbx_u_factors[] = {
622
     -15, -49,  64,   0, -15, -49,  64,   0, -15, -49,  64,   0, -15, -49,  64,   0
623
};
624
PRIM_ALIGN_128 static const BYTE rgbx_v_factors[] = {
625
      64, -58,  -6,   0,  64, -58,  -6,   0,  64, -58,  -6,   0,  64, -58,  -6,   0
626
};
627
*/
628
629
static inline void sse41_BGRX_TO_YUV(const BYTE* WINPR_RESTRICT pLine1, BYTE* WINPR_RESTRICT pYLine,
630
                                     BYTE* WINPR_RESTRICT pULine, BYTE* WINPR_RESTRICT pVLine)
631
0
{
632
0
  const BYTE r1 = pLine1[2];
633
0
  const BYTE g1 = pLine1[1];
634
0
  const BYTE b1 = pLine1[0];
635
636
0
  if (pYLine)
637
0
    pYLine[0] = RGB2Y(r1, g1, b1);
638
0
  if (pULine)
639
0
    pULine[0] = RGB2U(r1, g1, b1);
640
0
  if (pVLine)
641
0
    pVLine[0] = RGB2V(r1, g1, b1);
642
0
}
643
644
/* compute the luma (Y) component from a single rgb source line */
645
646
static inline void sse41_RGBToYUV420_BGRX_Y(const BYTE* WINPR_RESTRICT src, BYTE* dst, UINT32 width)
647
0
{
648
0
  const __m128i y_factors = BGRX_Y_FACTORS;
649
0
  const __m128i* argb = WINPR_PACKED_ALIGN_CAST(const __m128i*, src);
650
0
  __m128i* ydst = WINPR_PACKED_ALIGN_CAST(__m128i*, dst);
651
652
0
  UINT32 x = 0;
653
654
0
  for (; x < width - width % 16; x += 16)
655
0
  {
656
    /* store 16 rgba pixels in 4 128 bit registers */
657
0
    __m128i x0 = LOAD_SI128(argb++); // 1st 4 pixels
658
0
    {
659
0
      x0 = _mm_maddubs_epi16(x0, y_factors);
660
661
0
      __m128i x1 = LOAD_SI128(argb++); // 2nd 4 pixels
662
0
      x1 = _mm_maddubs_epi16(x1, y_factors);
663
0
      x0 = _mm_hadds_epi16(x0, x1);
664
0
      x0 = _mm_srli_epi16(x0, Y_SHIFT);
665
0
    }
666
667
0
    __m128i x2 = LOAD_SI128(argb++); // 3rd 4 pixels
668
0
    {
669
0
      x2 = _mm_maddubs_epi16(x2, y_factors);
670
671
0
      __m128i x3 = LOAD_SI128(argb++); // 4th 4 pixels
672
0
      x3 = _mm_maddubs_epi16(x3, y_factors);
673
0
      x2 = _mm_hadds_epi16(x2, x3);
674
0
      x2 = _mm_srli_epi16(x2, Y_SHIFT);
675
0
    }
676
677
0
    x0 = _mm_packus_epi16(x0, x2);
678
    /* save to y plane */
679
0
    STORE_SI128(ydst++, x0);
680
0
  }
681
682
0
  for (; x < width; x++)
683
0
  {
684
0
    sse41_BGRX_TO_YUV(&src[4ULL * x], &dst[x], nullptr, nullptr);
685
0
  }
686
0
}
687
688
/* compute the chrominance (UV) components from two rgb source lines */
689
690
static inline void sse41_RGBToYUV420_BGRX_UV(const BYTE* WINPR_RESTRICT src1,
691
                                             const BYTE* WINPR_RESTRICT src2,
692
                                             BYTE* WINPR_RESTRICT dst1, BYTE* WINPR_RESTRICT dst2,
693
                                             UINT32 width)
694
0
{
695
0
  const __m128i u_factors = BGRX_U_FACTORS;
696
0
  const __m128i v_factors = BGRX_V_FACTORS;
697
0
  const __m128i vector128 = CONST128_FACTORS;
698
699
0
  size_t x = 0;
700
701
0
  for (; x < width - width % 16; x += 16)
702
0
  {
703
0
    const __m128i* rgb1 = WINPR_PACKED_ALIGN_CAST(const __m128i*, &src1[4ULL * x]);
704
0
    const __m128i* rgb2 = WINPR_PACKED_ALIGN_CAST(const __m128i*, &src2[4ULL * x]);
705
0
    __m64* udst = WINPR_PACKED_ALIGN_CAST(__m64*, &dst1[x / 2]);
706
0
    __m64* vdst = WINPR_PACKED_ALIGN_CAST(__m64*, &dst2[x / 2]);
707
708
    /* subsample 16x2 pixels into 16x1 pixels */
709
0
    __m128i x0 = LOAD_SI128(&rgb1[0]);
710
0
    __m128i x4 = LOAD_SI128(&rgb2[0]);
711
0
    x0 = _mm_avg_epu8(x0, x4);
712
713
0
    __m128i x1 = LOAD_SI128(&rgb1[1]);
714
0
    x4 = LOAD_SI128(&rgb2[1]);
715
0
    x1 = _mm_avg_epu8(x1, x4);
716
717
0
    __m128i x2 = LOAD_SI128(&rgb1[2]);
718
0
    x4 = LOAD_SI128(&rgb2[2]);
719
0
    x2 = _mm_avg_epu8(x2, x4);
720
721
0
    __m128i x3 = LOAD_SI128(&rgb1[3]);
722
0
    x4 = LOAD_SI128(&rgb2[3]);
723
0
    x3 = _mm_avg_epu8(x3, x4);
724
725
    /* subsample these 16x1 pixels into 8x1 pixels */
726
    /**
727
     * shuffle controls
728
     * c = a[0],a[2],b[0],b[2] == 10 00 10 00 = 0x88
729
     * c = a[1],a[3],b[1],b[3] == 11 01 11 01 = 0xdd
730
     */
731
0
    x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0x88));
732
0
    x0 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x1), 0xdd));
733
0
    x0 = _mm_avg_epu8(x0, x4);
734
0
    x4 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0x88));
735
0
    x1 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x2), _mm_castsi128_ps(x3), 0xdd));
736
0
    x1 = _mm_avg_epu8(x1, x4);
737
    /* multiplications and subtotals */
738
0
    x2 = _mm_maddubs_epi16(x0, u_factors);
739
0
    x3 = _mm_maddubs_epi16(x1, u_factors);
740
0
    x4 = _mm_maddubs_epi16(x0, v_factors);
741
0
    __m128i x5 = _mm_maddubs_epi16(x1, v_factors);
742
    /* the total sums */
743
0
    x0 = _mm_hadd_epi16(x2, x3);
744
0
    x1 = _mm_hadd_epi16(x4, x5);
745
    /* shift the results */
746
0
    x0 = _mm_srai_epi16(x0, U_SHIFT);
747
0
    x1 = _mm_srai_epi16(x1, V_SHIFT);
748
    /* pack the 16 words into bytes */
749
0
    x0 = _mm_packs_epi16(x0, x1);
750
    /* add 128 */
751
0
    x0 = _mm_sub_epi8(x0, vector128);
752
    /* the lower 8 bytes go to the u plane */
753
0
    _mm_storel_pi(udst, _mm_castsi128_ps(x0));
754
    /* the upper 8 bytes go to the v plane */
755
0
    _mm_storeh_pi(vdst, _mm_castsi128_ps(x0));
756
0
  }
757
758
0
  for (; x < width - width % 2; x += 2)
759
0
  {
760
0
    BYTE u[4] = WINPR_C_ARRAY_INIT;
761
0
    BYTE v[4] = WINPR_C_ARRAY_INIT;
762
0
    sse41_BGRX_TO_YUV(&src1[4ULL * x], nullptr, &u[0], &v[0]);
763
0
    sse41_BGRX_TO_YUV(&src1[4ULL * (1ULL + x)], nullptr, &u[1], &v[1]);
764
0
    sse41_BGRX_TO_YUV(&src2[4ULL * x], nullptr, &u[2], &v[2]);
765
0
    sse41_BGRX_TO_YUV(&src2[4ULL * (1ULL + x)], nullptr, &u[3], &v[3]);
766
0
    const INT16 u4 = WINPR_ASSERTING_INT_CAST(INT16, (INT16)u[0] + u[1] + u[2] + u[3]);
767
0
    const INT16 uu = WINPR_ASSERTING_INT_CAST(INT16, u4 / 4);
768
0
    const BYTE u8 = CLIP(uu);
769
0
    dst1[x / 2] = u8;
770
771
0
    const INT16 v4 = WINPR_ASSERTING_INT_CAST(INT16, (INT16)v[0] + v[1] + v[2] + v[3]);
772
0
    const INT16 vu = WINPR_ASSERTING_INT_CAST(INT16, v4 / 4);
773
0
    const BYTE v8 = CLIP(vu);
774
0
    dst2[x / 2] = v8;
775
0
  }
776
0
}
777
778
static pstatus_t sse41_RGBToYUV420_BGRX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
779
                                        BYTE* WINPR_RESTRICT pDst[], const UINT32 dstStep[],
780
                                        const prim_size_t* WINPR_RESTRICT roi)
781
0
{
782
0
  if (roi->height < 1 || roi->width < 1)
783
0
  {
784
0
    return !PRIMITIVES_SUCCESS;
785
0
  }
786
787
0
  size_t y = 0;
788
0
  for (; y < roi->height - roi->height % 2; y += 2)
789
0
  {
790
0
    const BYTE* line1 = &pSrc[y * srcStep];
791
0
    const BYTE* line2 = &pSrc[(1ULL + y) * srcStep];
792
0
    BYTE* ydst1 = &pDst[0][y * dstStep[0]];
793
0
    BYTE* ydst2 = &pDst[0][(1ULL + y) * dstStep[0]];
794
0
    BYTE* udst = &pDst[1][y / 2 * dstStep[1]];
795
0
    BYTE* vdst = &pDst[2][y / 2 * dstStep[2]];
796
797
0
    sse41_RGBToYUV420_BGRX_UV(line1, line2, udst, vdst, roi->width);
798
0
    sse41_RGBToYUV420_BGRX_Y(line1, ydst1, roi->width);
799
0
    sse41_RGBToYUV420_BGRX_Y(line2, ydst2, roi->width);
800
0
  }
801
802
0
  for (; y < roi->height; y++)
803
0
  {
804
0
    const BYTE* line = &pSrc[y * srcStep];
805
0
    BYTE* ydst = &pDst[0][1ULL * y * dstStep[0]];
806
0
    sse41_RGBToYUV420_BGRX_Y(line, ydst, roi->width);
807
0
  }
808
809
0
  return PRIMITIVES_SUCCESS;
810
0
}
811
812
static pstatus_t sse41_RGBToYUV420(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
813
                                   UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[],
814
                                   const UINT32 dstStep[], const prim_size_t* WINPR_RESTRICT roi)
815
0
{
816
0
  switch (srcFormat)
817
0
  {
818
0
    case PIXEL_FORMAT_BGRX32:
819
0
    case PIXEL_FORMAT_BGRA32:
820
0
      return sse41_RGBToYUV420_BGRX(pSrc, srcStep, pDst, dstStep, roi);
821
822
0
    default:
823
0
      return generic->RGBToYUV420_8u_P3AC4R(pSrc, srcFormat, srcStep, pDst, dstStep, roi);
824
0
  }
825
0
}
826
827
/****************************************************************************/
828
/* sse41 RGB -> AVC444-YUV conversion                                      **/
829
/****************************************************************************/
830
831
static inline void sse41_RGBToAVC444YUV_BGRX_DOUBLE_ROW(
832
    const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd,
833
    BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
834
    BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
835
    BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
836
0
{
837
0
  const __m128i* argbEven = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcEven);
838
0
  const __m128i* argbOdd = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcOdd);
839
0
  const __m128i y_factors = BGRX_Y_FACTORS;
840
0
  const __m128i u_factors = BGRX_U_FACTORS;
841
0
  const __m128i v_factors = BGRX_V_FACTORS;
842
0
  const __m128i vector128 = CONST128_FACTORS;
843
844
0
  UINT32 x = 0;
845
0
  for (; x < width - width % 16; x += 16)
846
0
  {
847
    /* store 16 rgba pixels in 4 128 bit registers */
848
0
    const __m128i xe1 = LOAD_SI128(argbEven++); // 1st 4 pixels
849
0
    const __m128i xe2 = LOAD_SI128(argbEven++); // 2nd 4 pixels
850
0
    const __m128i xe3 = LOAD_SI128(argbEven++); // 3rd 4 pixels
851
0
    const __m128i xe4 = LOAD_SI128(argbEven++); // 4th 4 pixels
852
0
    const __m128i xo1 = LOAD_SI128(argbOdd++);  // 1st 4 pixels
853
0
    const __m128i xo2 = LOAD_SI128(argbOdd++);  // 2nd 4 pixels
854
0
    const __m128i xo3 = LOAD_SI128(argbOdd++);  // 3rd 4 pixels
855
0
    const __m128i xo4 = LOAD_SI128(argbOdd++);  // 4th 4 pixels
856
0
    {
857
      /* Y: multiplications with subtotals and horizontal sums */
858
0
      const __m128i ye1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, y_factors),
859
0
                                                        _mm_maddubs_epi16(xe2, y_factors)),
860
0
                                         Y_SHIFT);
861
0
      const __m128i ye2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, y_factors),
862
0
                                                        _mm_maddubs_epi16(xe4, y_factors)),
863
0
                                         Y_SHIFT);
864
0
      const __m128i ye = _mm_packus_epi16(ye1, ye2);
865
0
      const __m128i yo1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, y_factors),
866
0
                                                        _mm_maddubs_epi16(xo2, y_factors)),
867
0
                                         Y_SHIFT);
868
0
      const __m128i yo2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, y_factors),
869
0
                                                        _mm_maddubs_epi16(xo4, y_factors)),
870
0
                                         Y_SHIFT);
871
0
      const __m128i yo = _mm_packus_epi16(yo1, yo2);
872
      /* store y [b1] */
873
0
      STORE_SI128(b1Even, ye);
874
0
      b1Even += 16;
875
876
0
      if (b1Odd)
877
0
      {
878
0
        STORE_SI128(b1Odd, yo);
879
0
        b1Odd += 16;
880
0
      }
881
0
    }
882
0
    {
883
      /* We have now
884
       * 16 even U values in ue
885
       * 16 odd U values in uo
886
       *
887
       * We need to split these according to
888
       * 3.3.8.3.2 YUV420p Stream Combination for YUV444 mode */
889
0
      __m128i ue;
890
0
      __m128i uo = WINPR_C_ARRAY_INIT;
891
0
      {
892
0
        const __m128i ue1 =
893
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, u_factors),
894
0
                                          _mm_maddubs_epi16(xe2, u_factors)),
895
0
                           U_SHIFT);
896
0
        const __m128i ue2 =
897
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, u_factors),
898
0
                                          _mm_maddubs_epi16(xe4, u_factors)),
899
0
                           U_SHIFT);
900
0
        ue = _mm_sub_epi8(_mm_packs_epi16(ue1, ue2), vector128);
901
0
      }
902
903
0
      if (b1Odd)
904
0
      {
905
0
        const __m128i uo1 =
906
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, u_factors),
907
0
                                          _mm_maddubs_epi16(xo2, u_factors)),
908
0
                           U_SHIFT);
909
0
        const __m128i uo2 =
910
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, u_factors),
911
0
                                          _mm_maddubs_epi16(xo4, u_factors)),
912
0
                           U_SHIFT);
913
0
        uo = _mm_sub_epi8(_mm_packs_epi16(uo1, uo2), vector128);
914
0
      }
915
916
      /* Now we need the following storage distribution:
917
       * 2x   2y    -> b2
918
       * x    2y+1  -> b4
919
       * 2x+1 2y    -> b6 */
920
0
      if (b1Odd) /* b2 */
921
0
      {
922
0
        const __m128i ueh = _mm_unpackhi_epi8(ue, _mm_setzero_si128());
923
0
        const __m128i uoh = _mm_unpackhi_epi8(uo, _mm_setzero_si128());
924
0
        const __m128i hi = _mm_add_epi16(ueh, uoh);
925
0
        const __m128i uel = _mm_unpacklo_epi8(ue, _mm_setzero_si128());
926
0
        const __m128i uol = _mm_unpacklo_epi8(uo, _mm_setzero_si128());
927
0
        const __m128i lo = _mm_add_epi16(uel, uol);
928
0
        const __m128i added = _mm_hadd_epi16(lo, hi);
929
0
        const __m128i avg16 = _mm_srai_epi16(added, 2);
930
0
        const __m128i avg = _mm_packus_epi16(avg16, avg16);
931
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b2), avg);
932
0
      }
933
0
      else
934
0
      {
935
0
        const __m128i mask =
936
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
937
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
938
0
        const __m128i ud = _mm_shuffle_epi8(ue, mask);
939
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b2), ud);
940
0
      }
941
942
0
      b2 += 8;
943
944
0
      if (b1Odd) /* b4 */
945
0
      {
946
0
        STORE_SI128(b4, uo);
947
0
        b4 += 16;
948
0
      }
949
950
0
      {
951
        /* b6 */
952
0
        const __m128i mask =
953
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
954
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
955
0
        const __m128i ude = _mm_shuffle_epi8(ue, mask);
956
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b6), ude);
957
0
        b6 += 8;
958
0
      }
959
0
    }
960
0
    {
961
      /* We have now
962
       * 16 even V values in ue
963
       * 16 odd V values in uo
964
       *
965
       * We need to split these according to
966
       * 3.3.8.3.2 YUV420p Stream Combination for YUV444 mode */
967
0
      __m128i ve;
968
0
      __m128i vo = WINPR_C_ARRAY_INIT;
969
0
      {
970
0
        const __m128i ve1 =
971
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, v_factors),
972
0
                                          _mm_maddubs_epi16(xe2, v_factors)),
973
0
                           V_SHIFT);
974
0
        const __m128i ve2 =
975
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, v_factors),
976
0
                                          _mm_maddubs_epi16(xe4, v_factors)),
977
0
                           V_SHIFT);
978
0
        ve = _mm_sub_epi8(_mm_packs_epi16(ve1, ve2), vector128);
979
0
      }
980
981
0
      if (b1Odd)
982
0
      {
983
0
        const __m128i vo1 =
984
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, v_factors),
985
0
                                          _mm_maddubs_epi16(xo2, v_factors)),
986
0
                           V_SHIFT);
987
0
        const __m128i vo2 =
988
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, v_factors),
989
0
                                          _mm_maddubs_epi16(xo4, v_factors)),
990
0
                           V_SHIFT);
991
0
        vo = _mm_sub_epi8(_mm_packs_epi16(vo1, vo2), vector128);
992
0
      }
993
994
      /* Now we need the following storage distribution:
995
       * 2x   2y    -> b3
996
       * x    2y+1  -> b5
997
       * 2x+1 2y    -> b7 */
998
0
      if (b1Odd) /* b3 */
999
0
      {
1000
0
        const __m128i veh = _mm_unpackhi_epi8(ve, _mm_setzero_si128());
1001
0
        const __m128i voh = _mm_unpackhi_epi8(vo, _mm_setzero_si128());
1002
0
        const __m128i hi = _mm_add_epi16(veh, voh);
1003
0
        const __m128i vel = _mm_unpacklo_epi8(ve, _mm_setzero_si128());
1004
0
        const __m128i vol = _mm_unpacklo_epi8(vo, _mm_setzero_si128());
1005
0
        const __m128i lo = _mm_add_epi16(vel, vol);
1006
0
        const __m128i added = _mm_hadd_epi16(lo, hi);
1007
0
        const __m128i avg16 = _mm_srai_epi16(added, 2);
1008
0
        const __m128i avg = _mm_packus_epi16(avg16, avg16);
1009
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b3), avg);
1010
0
      }
1011
0
      else
1012
0
      {
1013
0
        const __m128i mask =
1014
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1015
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
1016
0
        const __m128i vd = _mm_shuffle_epi8(ve, mask);
1017
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b3), vd);
1018
0
      }
1019
1020
0
      b3 += 8;
1021
1022
0
      if (b1Odd) /* b5 */
1023
0
      {
1024
0
        STORE_SI128(b5, vo);
1025
0
        b5 += 16;
1026
0
      }
1027
1028
0
      {
1029
        /* b7 */
1030
0
        const __m128i mask =
1031
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1032
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1033
0
        const __m128i vde = _mm_shuffle_epi8(ve, mask);
1034
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, b7), vde);
1035
0
        b7 += 8;
1036
0
      }
1037
0
    }
1038
0
  }
1039
1040
0
  general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(x, srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4, b5, b6,
1041
0
                                         b7, width);
1042
0
}
1043
1044
static pstatus_t sse41_RGBToAVC444YUV_BGRX(const BYTE* WINPR_RESTRICT pSrc,
1045
                                           WINPR_ATTR_UNUSED UINT32 srcFormat, UINT32 srcStep,
1046
                                           BYTE* WINPR_RESTRICT pDst1[], const UINT32 dst1Step[],
1047
                                           BYTE* WINPR_RESTRICT pDst2[], const UINT32 dst2Step[],
1048
                                           const prim_size_t* WINPR_RESTRICT roi)
1049
0
{
1050
0
  if (roi->height < 1 || roi->width < 1)
1051
0
    return !PRIMITIVES_SUCCESS;
1052
1053
0
  size_t y = 0;
1054
0
  for (; y < roi->height - roi->height % 2; y += 2)
1055
0
  {
1056
0
    const BYTE* srcEven = pSrc + y * srcStep;
1057
0
    const BYTE* srcOdd = pSrc + (y + 1) * srcStep;
1058
0
    const size_t i = y >> 1;
1059
0
    const size_t n = (i & (size_t)~7) + i;
1060
0
    BYTE* b1Even = pDst1[0] + y * dst1Step[0];
1061
0
    BYTE* b1Odd = (b1Even + dst1Step[0]);
1062
0
    BYTE* b2 = pDst1[1] + (y / 2) * dst1Step[1];
1063
0
    BYTE* b3 = pDst1[2] + (y / 2) * dst1Step[2];
1064
0
    BYTE* b4 = pDst2[0] + 1ULL * dst2Step[0] * n;
1065
0
    BYTE* b5 = b4 + 8ULL * dst2Step[0];
1066
0
    BYTE* b6 = pDst2[1] + (y / 2) * dst2Step[1];
1067
0
    BYTE* b7 = pDst2[2] + (y / 2) * dst2Step[2];
1068
0
    sse41_RGBToAVC444YUV_BGRX_DOUBLE_ROW(srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4, b5, b6, b7,
1069
0
                                         roi->width);
1070
0
  }
1071
1072
0
  for (; y < roi->height; y++)
1073
0
  {
1074
0
    const BYTE* srcEven = pSrc + y * srcStep;
1075
0
    BYTE* b1Even = pDst1[0] + y * dst1Step[0];
1076
0
    BYTE* b2 = pDst1[1] + (y / 2) * dst1Step[1];
1077
0
    BYTE* b3 = pDst1[2] + (y / 2) * dst1Step[2];
1078
0
    BYTE* b6 = pDst2[1] + (y / 2) * dst2Step[1];
1079
0
    BYTE* b7 = pDst2[2] + (y / 2) * dst2Step[2];
1080
0
    general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(0, srcEven, nullptr, b1Even, nullptr, b2, b3,
1081
0
                                           nullptr, nullptr, b6, b7, roi->width);
1082
0
  }
1083
1084
0
  return PRIMITIVES_SUCCESS;
1085
0
}
1086
1087
static pstatus_t sse41_RGBToAVC444YUV(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1088
                                      UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[],
1089
                                      const UINT32 dst1Step[], BYTE* WINPR_RESTRICT pDst2[],
1090
                                      const UINT32 dst2Step[],
1091
                                      const prim_size_t* WINPR_RESTRICT roi)
1092
0
{
1093
0
  switch (srcFormat)
1094
0
  {
1095
0
    case PIXEL_FORMAT_BGRX32:
1096
0
    case PIXEL_FORMAT_BGRA32:
1097
0
      return sse41_RGBToAVC444YUV_BGRX(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1098
0
                                       dst2Step, roi);
1099
1100
0
    default:
1101
0
      return generic->RGBToAVC444YUV(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1102
0
                                     dst2Step, roi);
1103
0
  }
1104
0
}
1105
1106
/* Mapping of arguments:
1107
 *
1108
 * b1 [even lines] -> yLumaDstEven
1109
 * b1 [odd lines]  -> yLumaDstOdd
1110
 * b2              -> uLumaDst
1111
 * b3              -> vLumaDst
1112
 * b4              -> yChromaDst1
1113
 * b5              -> yChromaDst2
1114
 * b6              -> uChromaDst1
1115
 * b7              -> uChromaDst2
1116
 * b8              -> vChromaDst1
1117
 * b9              -> vChromaDst2
1118
 */
1119
static inline void sse41_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
1120
    const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd,
1121
    BYTE* WINPR_RESTRICT yLumaDstEven, BYTE* WINPR_RESTRICT yLumaDstOdd,
1122
    BYTE* WINPR_RESTRICT uLumaDst, BYTE* WINPR_RESTRICT vLumaDst,
1123
    BYTE* WINPR_RESTRICT yEvenChromaDst1, BYTE* WINPR_RESTRICT yEvenChromaDst2,
1124
    BYTE* WINPR_RESTRICT yOddChromaDst1, BYTE* WINPR_RESTRICT yOddChromaDst2,
1125
    BYTE* WINPR_RESTRICT uChromaDst1, BYTE* WINPR_RESTRICT uChromaDst2,
1126
    BYTE* WINPR_RESTRICT vChromaDst1, BYTE* WINPR_RESTRICT vChromaDst2, UINT32 width)
1127
0
{
1128
0
  const __m128i vector128 = CONST128_FACTORS;
1129
0
  const __m128i* argbEven = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcEven);
1130
0
  const __m128i* argbOdd = WINPR_PACKED_ALIGN_CAST(const __m128i*, srcOdd);
1131
1132
0
  UINT32 x = 0;
1133
0
  for (; x < width - width % 16; x += 16)
1134
0
  {
1135
    /* store 16 rgba pixels in 4 128 bit registers
1136
     * for even and odd rows.
1137
     */
1138
0
    const __m128i xe1 = LOAD_SI128(argbEven++); /* 1st 4 pixels */
1139
0
    const __m128i xe2 = LOAD_SI128(argbEven++); /* 2nd 4 pixels */
1140
0
    const __m128i xe3 = LOAD_SI128(argbEven++); /* 3rd 4 pixels */
1141
0
    const __m128i xe4 = LOAD_SI128(argbEven++); /* 4th 4 pixels */
1142
0
    const __m128i xo1 = LOAD_SI128(argbOdd++);  /* 1st 4 pixels */
1143
0
    const __m128i xo2 = LOAD_SI128(argbOdd++);  /* 2nd 4 pixels */
1144
0
    const __m128i xo3 = LOAD_SI128(argbOdd++);  /* 3rd 4 pixels */
1145
0
    const __m128i xo4 = LOAD_SI128(argbOdd++);  /* 4th 4 pixels */
1146
0
    {
1147
      /* Y: multiplications with subtotals and horizontal sums */
1148
0
      const __m128i y_factors = BGRX_Y_FACTORS;
1149
0
      const __m128i ye1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, y_factors),
1150
0
                                                        _mm_maddubs_epi16(xe2, y_factors)),
1151
0
                                         Y_SHIFT);
1152
0
      const __m128i ye2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, y_factors),
1153
0
                                                        _mm_maddubs_epi16(xe4, y_factors)),
1154
0
                                         Y_SHIFT);
1155
0
      const __m128i ye = _mm_packus_epi16(ye1, ye2);
1156
      /* store y [b1] */
1157
0
      STORE_SI128(yLumaDstEven, ye);
1158
0
      yLumaDstEven += 16;
1159
0
    }
1160
1161
0
    if (yLumaDstOdd)
1162
0
    {
1163
0
      const __m128i y_factors = BGRX_Y_FACTORS;
1164
0
      const __m128i yo1 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, y_factors),
1165
0
                                                        _mm_maddubs_epi16(xo2, y_factors)),
1166
0
                                         Y_SHIFT);
1167
0
      const __m128i yo2 = _mm_srli_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, y_factors),
1168
0
                                                        _mm_maddubs_epi16(xo4, y_factors)),
1169
0
                                         Y_SHIFT);
1170
0
      const __m128i yo = _mm_packus_epi16(yo1, yo2);
1171
0
      STORE_SI128(yLumaDstOdd, yo);
1172
0
      yLumaDstOdd += 16;
1173
0
    }
1174
1175
0
    {
1176
      /* We have now
1177
       * 16 even U values in ue
1178
       * 16 odd U values in uo
1179
       *
1180
       * We need to split these according to
1181
       * 3.3.8.3.3 YUV420p Stream Combination for YUV444v2 mode */
1182
      /* U: multiplications with subtotals and horizontal sums */
1183
0
      __m128i ue;
1184
0
      __m128i uo;
1185
0
      __m128i uavg;
1186
0
      {
1187
0
        const __m128i u_factors = BGRX_U_FACTORS;
1188
0
        const __m128i ue1 =
1189
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, u_factors),
1190
0
                                          _mm_maddubs_epi16(xe2, u_factors)),
1191
0
                           U_SHIFT);
1192
0
        const __m128i ue2 =
1193
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, u_factors),
1194
0
                                          _mm_maddubs_epi16(xe4, u_factors)),
1195
0
                           U_SHIFT);
1196
0
        const __m128i ueavg = _mm_hadd_epi16(ue1, ue2);
1197
0
        ue = _mm_sub_epi8(_mm_packs_epi16(ue1, ue2), vector128);
1198
0
        uavg = ueavg;
1199
0
      }
1200
0
      {
1201
0
        const __m128i u_factors = BGRX_U_FACTORS;
1202
0
        const __m128i uo1 =
1203
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, u_factors),
1204
0
                                          _mm_maddubs_epi16(xo2, u_factors)),
1205
0
                           U_SHIFT);
1206
0
        const __m128i uo2 =
1207
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, u_factors),
1208
0
                                          _mm_maddubs_epi16(xo4, u_factors)),
1209
0
                           U_SHIFT);
1210
0
        const __m128i uoavg = _mm_hadd_epi16(uo1, uo2);
1211
0
        uo = _mm_sub_epi8(_mm_packs_epi16(uo1, uo2), vector128);
1212
0
        uavg = _mm_add_epi16(uavg, uoavg);
1213
0
        uavg = _mm_srai_epi16(uavg, 2);
1214
0
        uavg = _mm_packs_epi16(uavg, uoavg);
1215
0
        uavg = _mm_sub_epi8(uavg, vector128);
1216
0
      }
1217
      /* Now we need the following storage distribution:
1218
       * 2x   2y    -> uLumaDst
1219
       * 2x+1  y    -> yChromaDst1
1220
       * 4x   2y+1  -> uChromaDst1
1221
       * 4x+2 2y+1  -> vChromaDst1 */
1222
0
      {
1223
0
        const __m128i mask =
1224
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1225
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1226
0
        const __m128i ude = _mm_shuffle_epi8(ue, mask);
1227
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yEvenChromaDst1), ude);
1228
0
        yEvenChromaDst1 += 8;
1229
0
      }
1230
1231
0
      if (yLumaDstOdd)
1232
0
      {
1233
0
        const __m128i mask =
1234
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1235
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1236
0
        const __m128i udo /* codespell:ignore udo */ = _mm_shuffle_epi8(uo, mask);
1237
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yOddChromaDst1),
1238
0
                         udo); // codespell:ignore udo
1239
0
        yOddChromaDst1 += 8;
1240
0
      }
1241
1242
0
      if (yLumaDstOdd)
1243
0
      {
1244
0
        const __m128i mask =
1245
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1246
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 10, 6, 2, 12, 8, 4, 0);
1247
0
        const __m128i ud = _mm_shuffle_epi8(uo, mask);
1248
0
        int* uDst1 = WINPR_PACKED_ALIGN_CAST(int*, uChromaDst1);
1249
0
        int* vDst1 = WINPR_PACKED_ALIGN_CAST(int*, vChromaDst1);
1250
0
        const int* src = (const int*)&ud;
1251
0
        _mm_stream_si32(uDst1, src[0]);
1252
0
        _mm_stream_si32(vDst1, src[1]);
1253
0
        uChromaDst1 += 4;
1254
0
        vChromaDst1 += 4;
1255
0
      }
1256
1257
0
      if (yLumaDstOdd)
1258
0
      {
1259
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, uLumaDst), uavg);
1260
0
        uLumaDst += 8;
1261
0
      }
1262
0
      else
1263
0
      {
1264
0
        const __m128i mask =
1265
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1266
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
1267
0
        const __m128i ud = _mm_shuffle_epi8(ue, mask);
1268
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, uLumaDst), ud);
1269
0
        uLumaDst += 8;
1270
0
      }
1271
0
    }
1272
1273
0
    {
1274
      /* V: multiplications with subtotals and horizontal sums */
1275
0
      __m128i ve;
1276
0
      __m128i vo;
1277
0
      __m128i vavg;
1278
0
      {
1279
0
        const __m128i v_factors = BGRX_V_FACTORS;
1280
0
        const __m128i ve1 =
1281
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe1, v_factors),
1282
0
                                          _mm_maddubs_epi16(xe2, v_factors)),
1283
0
                           V_SHIFT);
1284
0
        const __m128i ve2 =
1285
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xe3, v_factors),
1286
0
                                          _mm_maddubs_epi16(xe4, v_factors)),
1287
0
                           V_SHIFT);
1288
0
        const __m128i veavg = _mm_hadd_epi16(ve1, ve2);
1289
0
        ve = _mm_sub_epi8(_mm_packs_epi16(ve1, ve2), vector128);
1290
0
        vavg = veavg;
1291
0
      }
1292
0
      {
1293
0
        const __m128i v_factors = BGRX_V_FACTORS;
1294
0
        const __m128i vo1 =
1295
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo1, v_factors),
1296
0
                                          _mm_maddubs_epi16(xo2, v_factors)),
1297
0
                           V_SHIFT);
1298
0
        const __m128i vo2 =
1299
0
            _mm_srai_epi16(_mm_hadd_epi16(_mm_maddubs_epi16(xo3, v_factors),
1300
0
                                          _mm_maddubs_epi16(xo4, v_factors)),
1301
0
                           V_SHIFT);
1302
0
        const __m128i voavg = _mm_hadd_epi16(vo1, vo2);
1303
0
        vo = _mm_sub_epi8(_mm_packs_epi16(vo1, vo2), vector128);
1304
0
        vavg = _mm_add_epi16(vavg, voavg);
1305
0
        vavg = _mm_srai_epi16(vavg, 2);
1306
0
        vavg = _mm_packs_epi16(vavg, voavg);
1307
0
        vavg = _mm_sub_epi8(vavg, vector128);
1308
0
      }
1309
      /* Now we need the following storage distribution:
1310
       * 2x   2y    -> vLumaDst
1311
       * 2x+1  y    -> yChromaDst2
1312
       * 4x   2y+1  -> uChromaDst2
1313
       * 4x+2 2y+1  -> vChromaDst2 */
1314
0
      {
1315
0
        const __m128i mask =
1316
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1317
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1318
0
        __m128i vde = _mm_shuffle_epi8(ve, mask);
1319
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yEvenChromaDst2), vde);
1320
0
        yEvenChromaDst2 += 8;
1321
0
      }
1322
1323
0
      if (yLumaDstOdd)
1324
0
      {
1325
0
        const __m128i mask =
1326
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1327
0
                         (char)0x80, (char)0x80, (char)0x80, 15, 13, 11, 9, 7, 5, 3, 1);
1328
0
        __m128i vdo = _mm_shuffle_epi8(vo, mask);
1329
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, yOddChromaDst2), vdo);
1330
0
        yOddChromaDst2 += 8;
1331
0
      }
1332
1333
0
      if (yLumaDstOdd)
1334
0
      {
1335
0
        const __m128i mask =
1336
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1337
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 10, 6, 2, 12, 8, 4, 0);
1338
0
        const __m128i vd = _mm_shuffle_epi8(vo, mask);
1339
0
        int* uDst2 = WINPR_PACKED_ALIGN_CAST(int*, uChromaDst2);
1340
0
        int* vDst2 = WINPR_PACKED_ALIGN_CAST(int*, vChromaDst2);
1341
0
        const int* src = (const int*)&vd;
1342
0
        _mm_stream_si32(uDst2, src[0]);
1343
0
        _mm_stream_si32(vDst2, src[1]);
1344
0
        uChromaDst2 += 4;
1345
0
        vChromaDst2 += 4;
1346
0
      }
1347
1348
0
      if (yLumaDstOdd)
1349
0
      {
1350
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, vLumaDst), vavg);
1351
0
        vLumaDst += 8;
1352
0
      }
1353
0
      else
1354
0
      {
1355
0
        const __m128i mask =
1356
0
            _mm_set_epi8((char)0x80, (char)0x80, (char)0x80, (char)0x80, (char)0x80,
1357
0
                         (char)0x80, (char)0x80, (char)0x80, 14, 12, 10, 8, 6, 4, 2, 0);
1358
0
        __m128i vd = _mm_shuffle_epi8(ve, mask);
1359
0
        _mm_storel_epi64(WINPR_PACKED_ALIGN_CAST(__m128i*, vLumaDst), vd);
1360
0
        vLumaDst += 8;
1361
0
      }
1362
0
    }
1363
0
  }
1364
1365
0
  general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(x, srcEven, srcOdd, yLumaDstEven, yLumaDstOdd,
1366
0
                                           uLumaDst, vLumaDst, yEvenChromaDst1, yEvenChromaDst2,
1367
0
                                           yOddChromaDst1, yOddChromaDst2, uChromaDst1,
1368
0
                                           uChromaDst2, vChromaDst1, vChromaDst2, width);
1369
0
}
1370
1371
static pstatus_t sse41_RGBToAVC444YUVv2_BGRX(const BYTE* WINPR_RESTRICT pSrc,
1372
                                             WINPR_ATTR_UNUSED UINT32 srcFormat, UINT32 srcStep,
1373
                                             BYTE* WINPR_RESTRICT pDst1[], const UINT32 dst1Step[],
1374
                                             BYTE* WINPR_RESTRICT pDst2[], const UINT32 dst2Step[],
1375
                                             const prim_size_t* WINPR_RESTRICT roi)
1376
0
{
1377
0
  if (roi->height < 1 || roi->width < 1)
1378
0
    return !PRIMITIVES_SUCCESS;
1379
1380
0
  size_t y = 0;
1381
0
  for (; y < roi->height - roi->height % 2; y += 2)
1382
0
  {
1383
0
    const BYTE* srcEven = (pSrc + y * srcStep);
1384
0
    const BYTE* srcOdd = (srcEven + srcStep);
1385
0
    BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
1386
0
    BYTE* dstLumaYOdd = (dstLumaYEven + dst1Step[0]);
1387
0
    BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
1388
0
    BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
1389
0
    BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
1390
0
    BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
1391
0
    BYTE* dstOddChromaY1 = dstEvenChromaY1 + dst2Step[0];
1392
0
    BYTE* dstOddChromaY2 = dstEvenChromaY2 + dst2Step[0];
1393
0
    BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
1394
0
    BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
1395
0
    BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
1396
0
    BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
1397
0
    sse41_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(srcEven, srcOdd, dstLumaYEven, dstLumaYOdd, dstLumaU,
1398
0
                                           dstLumaV, dstEvenChromaY1, dstEvenChromaY2,
1399
0
                                           dstOddChromaY1, dstOddChromaY2, dstChromaU1,
1400
0
                                           dstChromaU2, dstChromaV1, dstChromaV2, roi->width);
1401
0
  }
1402
1403
0
  for (; y < roi->height; y++)
1404
0
  {
1405
0
    const BYTE* srcEven = (pSrc + y * srcStep);
1406
0
    BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
1407
0
    BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
1408
0
    BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
1409
0
    BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
1410
0
    BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
1411
0
    BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
1412
0
    BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
1413
0
    BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
1414
0
    BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
1415
0
    general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(0, srcEven, nullptr, dstLumaYEven, nullptr,
1416
0
                                             dstLumaU, dstLumaV, dstEvenChromaY1,
1417
0
                                             dstEvenChromaY2, nullptr, nullptr, dstChromaU1,
1418
0
                                             dstChromaU2, dstChromaV1, dstChromaV2, roi->width);
1419
0
  }
1420
1421
0
  return PRIMITIVES_SUCCESS;
1422
0
}
1423
1424
static pstatus_t sse41_RGBToAVC444YUVv2(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1425
                                        UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[],
1426
                                        const UINT32 dst1Step[], BYTE* WINPR_RESTRICT pDst2[],
1427
                                        const UINT32 dst2Step[],
1428
                                        const prim_size_t* WINPR_RESTRICT roi)
1429
0
{
1430
0
  switch (srcFormat)
1431
0
  {
1432
0
    case PIXEL_FORMAT_BGRX32:
1433
0
    case PIXEL_FORMAT_BGRA32:
1434
0
      return sse41_RGBToAVC444YUVv2_BGRX(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1435
0
                                         dst2Step, roi);
1436
1437
0
    default:
1438
0
      return generic->RGBToAVC444YUVv2(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1439
0
                                       dst2Step, roi);
1440
0
  }
1441
0
}
1442
1443
static pstatus_t sse41_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[], const UINT32 srcStep[],
1444
                                    BYTE* WINPR_RESTRICT pDstRaw[], const UINT32 dstStep[],
1445
                                    const RECTANGLE_16* WINPR_RESTRICT roi)
1446
0
{
1447
0
  const UINT32 nWidth = roi->right - roi->left;
1448
0
  const UINT32 nHeight = roi->bottom - roi->top;
1449
0
  const UINT32 halfWidth = (nWidth + 1) / 2;
1450
0
  const UINT32 halfPad = halfWidth % 16;
1451
0
  const UINT32 halfHeight = (nHeight + 1) / 2;
1452
0
  const UINT32 oddY = 1;
1453
0
  const UINT32 evenY = 0;
1454
0
  const UINT32 oddX = 1;
1455
0
  const UINT32 evenX = 0;
1456
0
  const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
1457
0
                        pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
1458
0
                        pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
1459
0
  BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
1460
0
                  pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
1461
0
                  pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
1462
1463
  /* Y data is already here... */
1464
  /* B1 */
1465
0
  for (size_t y = 0; y < nHeight; y++)
1466
0
  {
1467
0
    const BYTE* Ym = pSrc[0] + y * srcStep[0];
1468
0
    BYTE* pY = pDst[0] + y * dstStep[0];
1469
0
    memcpy(pY, Ym, nWidth);
1470
0
  }
1471
1472
  /* The first half of U, V are already here part of this frame. */
1473
  /* B2 and B3 */
1474
0
  for (size_t y = 0; y < halfHeight; y++)
1475
0
  {
1476
0
    const size_t val2y = (2 * y + evenY);
1477
0
    const size_t val2y1 = val2y + oddY;
1478
0
    const BYTE* Um = pSrc[1] + 1ULL * srcStep[1] * y;
1479
0
    const BYTE* Vm = pSrc[2] + 1ULL * srcStep[2] * y;
1480
0
    BYTE* pU = pDst[1] + 1ULL * dstStep[1] * val2y;
1481
0
    BYTE* pV = pDst[2] + 1ULL * dstStep[2] * val2y;
1482
0
    BYTE* pU1 = pDst[1] + 1ULL * dstStep[1] * val2y1;
1483
0
    BYTE* pV1 = pDst[2] + 1ULL * dstStep[2] * val2y1;
1484
1485
0
    size_t x = 0;
1486
0
    for (; x < halfWidth - halfPad; x += 16)
1487
0
    {
1488
0
      const __m128i unpackHigh = _mm_set_epi8(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0);
1489
0
      const __m128i unpackLow =
1490
0
          _mm_set_epi8(15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8);
1491
0
      {
1492
0
        const __m128i u = LOAD_SI128(&Um[x]);
1493
0
        const __m128i uHigh = _mm_shuffle_epi8(u, unpackHigh);
1494
0
        const __m128i uLow = _mm_shuffle_epi8(u, unpackLow);
1495
0
        STORE_SI128(&pU[2ULL * x], uHigh);
1496
0
        STORE_SI128(&pU[2ULL * x + 16], uLow);
1497
0
        STORE_SI128(&pU1[2ULL * x], uHigh);
1498
0
        STORE_SI128(&pU1[2ULL * x + 16], uLow);
1499
0
      }
1500
0
      {
1501
0
        const __m128i u = LOAD_SI128(&Vm[x]);
1502
0
        const __m128i uHigh = _mm_shuffle_epi8(u, unpackHigh);
1503
0
        const __m128i uLow = _mm_shuffle_epi8(u, unpackLow);
1504
0
        STORE_SI128(&pV[2 * x], uHigh);
1505
0
        STORE_SI128(&pV[2 * x + 16], uLow);
1506
0
        STORE_SI128(&pV1[2 * x], uHigh);
1507
0
        STORE_SI128(&pV1[2 * x + 16], uLow);
1508
0
      }
1509
0
    }
1510
1511
0
    for (; x < halfWidth; x++)
1512
0
    {
1513
0
      const size_t val2x = 2 * x + evenX;
1514
0
      const size_t val2x1 = val2x + oddX;
1515
0
      pU[val2x] = Um[x];
1516
0
      pV[val2x] = Vm[x];
1517
0
      pU[val2x1] = Um[x];
1518
0
      pV[val2x1] = Vm[x];
1519
0
      pU1[val2x] = Um[x];
1520
0
      pV1[val2x] = Vm[x];
1521
0
      pU1[val2x1] = Um[x];
1522
0
      pV1[val2x1] = Vm[x];
1523
0
    }
1524
0
  }
1525
1526
0
  return PRIMITIVES_SUCCESS;
1527
0
}
1528
1529
static pstatus_t sse41_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
1530
                                        const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDstRaw[3],
1531
                                        const UINT32 dstStep[3],
1532
                                        const RECTANGLE_16* WINPR_RESTRICT roi)
1533
0
{
1534
0
  const UINT32 mod = 16;
1535
0
  UINT32 uY = 0;
1536
0
  UINT32 vY = 0;
1537
0
  const UINT32 nWidth = roi->right - roi->left;
1538
0
  const UINT32 nHeight = roi->bottom - roi->top;
1539
0
  const UINT32 halfWidth = (nWidth + 1) / 2;
1540
0
  const UINT32 halfPad = halfWidth % 16;
1541
0
  const UINT32 halfHeight = (nHeight + 1) / 2;
1542
0
  const UINT32 oddY = 1;
1543
0
  const UINT32 evenY = 0;
1544
0
  const UINT32 oddX = 1;
1545
  /* The auxiliary frame is aligned to multiples of 16x16.
1546
   * We need the padded height for B4 and B5 conversion. */
1547
0
  const UINT32 padHeight = nHeight + 16 - nHeight % 16;
1548
0
  const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
1549
0
                        pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
1550
0
                        pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
1551
0
  BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
1552
0
                  pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
1553
0
                  pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
1554
0
  const __m128i zero = _mm_setzero_si128();
1555
0
  const __m128i mask = _mm_set_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0,
1556
0
                                    (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80);
1557
1558
  /* The second half of U and V is a bit more tricky... */
1559
  /* B4 and B5 */
1560
0
  for (size_t y = 0; y < padHeight; y++)
1561
0
  {
1562
0
    const BYTE* Ya = pSrc[0] + 1ULL * srcStep[0] * y;
1563
0
    BYTE* pX = nullptr;
1564
1565
0
    if ((y) % mod < (mod + 1) / 2)
1566
0
    {
1567
0
      const UINT32 pos = (2 * uY++ + oddY);
1568
1569
0
      if (pos >= nHeight)
1570
0
        continue;
1571
1572
0
      pX = pDst[1] + 1ULL * dstStep[1] * pos;
1573
0
    }
1574
0
    else
1575
0
    {
1576
0
      const UINT32 pos = (2 * vY++ + oddY);
1577
1578
0
      if (pos >= nHeight)
1579
0
        continue;
1580
1581
0
      pX = pDst[2] + 1ULL * dstStep[2] * pos;
1582
0
    }
1583
1584
0
    if (y < nHeight)
1585
0
      memcpy(pX, Ya, nWidth);
1586
0
  }
1587
1588
  /* B6 and B7 */
1589
0
  for (size_t y = 0; y < halfHeight; y++)
1590
0
  {
1591
0
    const size_t val2y = (y * 2 + evenY);
1592
0
    const BYTE* Ua = pSrc[1] + srcStep[1] * y;
1593
0
    const BYTE* Va = pSrc[2] + srcStep[2] * y;
1594
0
    BYTE* pU = pDst[1] + dstStep[1] * val2y;
1595
0
    BYTE* pV = pDst[2] + dstStep[2] * val2y;
1596
1597
0
    size_t x = 0;
1598
0
    for (; x < halfWidth - halfPad; x += 16)
1599
0
    {
1600
0
      {
1601
0
        const __m128i u = LOAD_SI128(&Ua[x]);
1602
0
        const __m128i u2 = _mm_unpackhi_epi8(u, zero);
1603
0
        const __m128i u1 = _mm_unpacklo_epi8(u, zero);
1604
        /* the mask selects the even bytes of u1/u2, which carry the
1605
         * samples; B6 goes to the odd columns 2x + 1 */
1606
0
        _mm_maskmoveu_si128(u1, mask, (char*)&pU[2 * x + 1]);
1607
0
        _mm_maskmoveu_si128(u2, mask, (char*)&pU[2 * x + 17]);
1608
0
      }
1609
0
      {
1610
0
        const __m128i u = LOAD_SI128(&Va[x]);
1611
0
        const __m128i u2 = _mm_unpackhi_epi8(u, zero);
1612
0
        const __m128i u1 = _mm_unpacklo_epi8(u, zero);
1613
        /* B7 likewise to the odd columns */
1614
0
        _mm_maskmoveu_si128(u1, mask, (char*)&pV[2 * x + 1]);
1615
0
        _mm_maskmoveu_si128(u2, mask, (char*)&pV[2 * x + 17]);
1616
0
      }
1617
0
    }
1618
1619
0
    for (; x < halfWidth; x++)
1620
0
    {
1621
0
      const size_t val2x1 = (x * 2ULL + oddX);
1622
0
      pU[val2x1] = Ua[x];
1623
0
      pV[val2x1] = Va[x];
1624
0
    }
1625
0
  }
1626
1627
0
  return PRIMITIVES_SUCCESS;
1628
0
}
1629
1630
static pstatus_t sse41_ChromaV2ToYUV444(const BYTE* WINPR_RESTRICT pSrc[3], const UINT32 srcStep[3],
1631
                                        UINT32 nTotalWidth, WINPR_ATTR_UNUSED UINT32 nTotalHeight,
1632
                                        BYTE* WINPR_RESTRICT pDst[3], const UINT32 dstStep[3],
1633
                                        const RECTANGLE_16* WINPR_RESTRICT roi)
1634
0
{
1635
0
  const UINT32 nWidth = roi->right - roi->left;
1636
0
  const UINT32 nHeight = roi->bottom - roi->top;
1637
0
  const UINT32 halfWidth = (nWidth + 1) / 2;
1638
0
  const UINT32 halfPad = halfWidth % 16;
1639
0
  const UINT32 halfHeight = (nHeight + 1) / 2;
1640
0
  const UINT32 quaterWidth = (nWidth + 3) / 4;
1641
0
  const UINT32 quaterPad = quaterWidth % 16;
1642
0
  const __m128i zero = _mm_setzero_si128();
1643
0
  const __m128i mask = _mm_set_epi8((char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0,
1644
0
                                    (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0);
1645
0
  const __m128i mask2 = _mm_set_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80,
1646
0
                                     0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80);
1647
0
  const __m128i shuffle1 =
1648
0
      _mm_set_epi8((char)0x80, 15, (char)0x80, 14, (char)0x80, 13, (char)0x80, 12, (char)0x80, 11,
1649
0
                   (char)0x80, 10, (char)0x80, 9, (char)0x80, 8);
1650
0
  const __m128i shuffle2 =
1651
0
      _mm_set_epi8((char)0x80, 7, (char)0x80, 6, (char)0x80, 5, (char)0x80, 4, (char)0x80, 3,
1652
0
                   (char)0x80, 2, (char)0x80, 1, (char)0x80, 0);
1653
1654
  /* B4 and B5: odd UV values for width/2, height */
1655
0
  for (size_t y = 0; y < nHeight; y++)
1656
0
  {
1657
0
    const size_t yTop = y + roi->top;
1658
0
    const BYTE* pYaU = pSrc[0] + srcStep[0] * yTop + roi->left / 2;
1659
0
    const BYTE* pYaV = pYaU + nTotalWidth / 2;
1660
0
    BYTE* pU = pDst[1] + 1ULL * dstStep[1] * yTop + roi->left;
1661
0
    BYTE* pV = pDst[2] + 1ULL * dstStep[2] * yTop + roi->left;
1662
1663
0
    size_t x = 0;
1664
0
    for (; x < halfWidth - halfPad; x += 16)
1665
0
    {
1666
0
      {
1667
0
        const __m128i u = LOAD_SI128(&pYaU[x]);
1668
0
        const __m128i u2 = _mm_unpackhi_epi8(zero, u);
1669
0
        const __m128i u1 = _mm_unpacklo_epi8(zero, u);
1670
0
        _mm_maskmoveu_si128(u1, mask, (char*)&pU[2 * x]);
1671
0
        _mm_maskmoveu_si128(u2, mask, (char*)&pU[2 * x + 16]);
1672
0
      }
1673
0
      {
1674
0
        const __m128i v = LOAD_SI128(&pYaV[x]);
1675
0
        const __m128i v2 = _mm_unpackhi_epi8(zero, v);
1676
0
        const __m128i v1 = _mm_unpacklo_epi8(zero, v);
1677
0
        _mm_maskmoveu_si128(v1, mask, (char*)&pV[2 * x]);
1678
0
        _mm_maskmoveu_si128(v2, mask, (char*)&pV[2 * x + 16]);
1679
0
      }
1680
0
    }
1681
1682
0
    for (; x < halfWidth; x++)
1683
0
    {
1684
0
      const size_t odd = 2ULL * x + 1;
1685
0
      pU[odd] = pYaU[x];
1686
0
      pV[odd] = pYaV[x];
1687
0
    }
1688
0
  }
1689
1690
  /* B6 - B9 */
1691
0
  for (size_t y = 0; y < halfHeight; y++)
1692
0
  {
1693
0
    const BYTE* pUaU = pSrc[1] + srcStep[1] * (y + roi->top / 2) + roi->left / 4;
1694
0
    const BYTE* pUaV = pUaU + nTotalWidth / 4;
1695
0
    const BYTE* pVaU = pSrc[2] + srcStep[2] * (y + roi->top / 2) + roi->left / 4;
1696
0
    const BYTE* pVaV = pVaU + nTotalWidth / 4;
1697
0
    BYTE* pU = pDst[1] + dstStep[1] * (2 * y + 1 + roi->top) + roi->left;
1698
0
    BYTE* pV = pDst[2] + dstStep[2] * (2 * y + 1 + roi->top) + roi->left;
1699
1700
0
    UINT32 x = 0;
1701
0
    for (; x < quaterWidth - quaterPad; x += 16)
1702
0
    {
1703
0
      {
1704
0
        const __m128i uU = LOAD_SI128(&pUaU[x]);
1705
0
        const __m128i uV = LOAD_SI128(&pVaU[x]);
1706
0
        const __m128i uHigh = _mm_unpackhi_epi8(uU, uV);
1707
0
        const __m128i uLow = _mm_unpacklo_epi8(uU, uV);
1708
0
        const __m128i u1 = _mm_shuffle_epi8(uLow, shuffle2);
1709
0
        const __m128i u2 = _mm_shuffle_epi8(uLow, shuffle1);
1710
0
        const __m128i u3 = _mm_shuffle_epi8(uHigh, shuffle2);
1711
0
        const __m128i u4 = _mm_shuffle_epi8(uHigh, shuffle1);
1712
0
        _mm_maskmoveu_si128(u1, mask2, (char*)&pU[4 * x + 0]);
1713
0
        _mm_maskmoveu_si128(u2, mask2, (char*)&pU[4 * x + 16]);
1714
0
        _mm_maskmoveu_si128(u3, mask2, (char*)&pU[4 * x + 32]);
1715
0
        _mm_maskmoveu_si128(u4, mask2, (char*)&pU[4 * x + 48]);
1716
0
      }
1717
0
      {
1718
0
        const __m128i vU = LOAD_SI128(&pUaV[x]);
1719
0
        const __m128i vV = LOAD_SI128(&pVaV[x]);
1720
0
        const __m128i vHigh = _mm_unpackhi_epi8(vU, vV);
1721
0
        const __m128i vLow = _mm_unpacklo_epi8(vU, vV);
1722
0
        const __m128i v1 = _mm_shuffle_epi8(vLow, shuffle2);
1723
0
        const __m128i v2 = _mm_shuffle_epi8(vLow, shuffle1);
1724
0
        const __m128i v3 = _mm_shuffle_epi8(vHigh, shuffle2);
1725
0
        const __m128i v4 = _mm_shuffle_epi8(vHigh, shuffle1);
1726
0
        _mm_maskmoveu_si128(v1, mask2, (char*)&pV[4 * x + 0]);
1727
0
        _mm_maskmoveu_si128(v2, mask2, (char*)&pV[4 * x + 16]);
1728
0
        _mm_maskmoveu_si128(v3, mask2, (char*)&pV[4 * x + 32]);
1729
0
        _mm_maskmoveu_si128(v4, mask2, (char*)&pV[4 * x + 48]);
1730
0
      }
1731
0
    }
1732
1733
0
    for (; x < quaterWidth; x++)
1734
0
    {
1735
0
      pU[4 * x + 0] = pUaU[x];
1736
0
      pV[4 * x + 0] = pUaV[x];
1737
0
      pU[4 * x + 2] = pVaU[x];
1738
0
      pV[4 * x + 2] = pVaV[x];
1739
0
    }
1740
0
  }
1741
1742
0
  return PRIMITIVES_SUCCESS;
1743
0
}
1744
1745
static pstatus_t sse41_YUV420CombineToYUV444(avc444_frame_type type,
1746
                                             const BYTE* WINPR_RESTRICT pSrc[3],
1747
                                             const UINT32 srcStep[3], UINT32 nWidth, UINT32 nHeight,
1748
                                             BYTE* WINPR_RESTRICT pDst[3], const UINT32 dstStep[3],
1749
                                             const RECTANGLE_16* WINPR_RESTRICT roi)
1750
0
{
1751
0
  if (!pSrc || !pSrc[0] || !pSrc[1] || !pSrc[2])
1752
0
    return -1;
1753
1754
0
  if (!pDst || !pDst[0] || !pDst[1] || !pDst[2])
1755
0
    return -1;
1756
1757
0
  if (!roi)
1758
0
    return -1;
1759
1760
0
  switch (type)
1761
0
  {
1762
0
    case AVC444_LUMA:
1763
0
      return sse41_LumaToYUV444(pSrc, srcStep, pDst, dstStep, roi);
1764
1765
0
    case AVC444_CHROMAv1:
1766
0
      return sse41_ChromaV1ToYUV444(pSrc, srcStep, pDst, dstStep, roi);
1767
1768
0
    case AVC444_CHROMAv2:
1769
0
      return sse41_ChromaV2ToYUV444(pSrc, srcStep, nWidth, nHeight, pDst, dstStep, roi);
1770
1771
0
    default:
1772
0
      return -1;
1773
0
  }
1774
0
}
1775
#endif
1776
1777
void primitives_init_YUV_sse41_int(primitives_t* WINPR_RESTRICT prims)
1778
2
{
1779
2
#if defined(SSE_AVX_INTRINSICS_ENABLED)
1780
2
  generic = primitives_get_generic();
1781
1782
2
  WLog_VRB(PRIM_TAG, "SSE3/sse41 optimizations");
1783
2
  prims->RGBToYUV420_8u_P3AC4R = sse41_RGBToYUV420;
1784
2
  prims->RGBToAVC444YUV = sse41_RGBToAVC444YUV;
1785
2
  prims->RGBToAVC444YUVv2 = sse41_RGBToAVC444YUVv2;
1786
2
  prims->YUV420ToRGB_8u_P3AC4R = sse41_YUV420ToRGB;
1787
2
  prims->YUV444ToRGB_8u_P3AC4R = sse41_YUV444ToRGB_8u_P3AC4R;
1788
2
  prims->I444ToRGB_8u = sse41_I444ToRGB_8u;
1789
2
  prims->YUV420CombineToYUV444 = sse41_YUV420CombineToYUV444;
1790
#else
1791
  WLog_VRB(PRIM_TAG, "undefined WITH_SIMD or sse41 intrinsics not available");
1792
  WINPR_UNUSED(prims);
1793
#endif
1794
2
}