FreeRDP
Loading...
Searching...
No Matches
prim_YUV.c
1
24#include <winpr/wtypes.h>
25#include <winpr/assert.h>
26#include <winpr/cast.h>
27
28#include <freerdp/config.h>
29
30#include <freerdp/types.h>
31#include <freerdp/primitives.h>
32#include <freerdp/codec/color.h>
33#include "prim_internal.h"
34#include "prim_YUV.h"
35
36static inline pstatus_t general_LumaToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
37 const UINT32 srcStep[3],
38 BYTE* WINPR_RESTRICT pDstRaw[3],
39 const UINT32 dstStep[3],
40 const RECTANGLE_16* WINPR_RESTRICT roi)
41{
42 const UINT32 nWidth = roi->right - roi->left;
43 const UINT32 nHeight = roi->bottom - roi->top;
44 const UINT32 halfWidth = (nWidth + 1) / 2;
45 const UINT32 halfHeight = (nHeight + 1) / 2;
46 const UINT32 oddY = 1;
47 const UINT32 evenY = 0;
48 const UINT32 oddX = 1;
49 const UINT32 evenX = 0;
50 const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
51 pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
52 pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
53 BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
54 pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
55 pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
56
57 /* Y data is already here... */
58 /* B1 */
59 for (size_t y = 0; y < nHeight; y++)
60 {
61 const BYTE* Ym = pSrc[0] + y * srcStep[0];
62 BYTE* pY = pDst[0] + dstStep[0] * y;
63 memcpy(pY, Ym, nWidth);
64 }
65
66 /* The first half of U, V are already here part of this frame. */
67 /* B2 and B3 */
68 for (UINT32 y = 0; y < halfHeight; y++)
69 {
70 const UINT32 val2y = (2UL * y + evenY);
71 const UINT32 val2y1 = val2y + oddY;
72 const BYTE* Um = pSrc[1] + 1ULL * y * srcStep[1];
73 const BYTE* Vm = pSrc[2] + 1ULL * y * srcStep[2];
74 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * val2y;
75 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * val2y;
76 BYTE* pU1 = pDst[1] + 1ULL * dstStep[1] * val2y1;
77 BYTE* pV1 = pDst[2] + 1ULL * dstStep[2] * val2y1;
78
79 for (UINT32 x = 0; x < halfWidth; x++)
80 {
81 const UINT32 val2x = 2UL * x + evenX;
82 const UINT32 val2x1 = val2x + oddX;
83 pU[val2x] = Um[x];
84 pV[val2x] = Vm[x];
85 pU[val2x1] = Um[x];
86 pV[val2x1] = Vm[x];
87 pU1[val2x] = Um[x];
88 pV1[val2x] = Vm[x];
89 pU1[val2x1] = Um[x];
90 pV1[val2x1] = Vm[x];
91 }
92 }
93
94 return PRIMITIVES_SUCCESS;
95}
96
97static inline pstatus_t general_ChromaV1ToYUV444(const BYTE* WINPR_RESTRICT pSrcRaw[3],
98 const UINT32 srcStep[3],
99 BYTE* WINPR_RESTRICT pDstRaw[3],
100 const UINT32 dstStep[3],
101 const RECTANGLE_16* WINPR_RESTRICT roi)
102{
103 const UINT32 mod = 16;
104 UINT32 uY = 0;
105 UINT32 vY = 0;
106 const UINT32 nWidth = roi->right - roi->left;
107 const UINT32 nHeight = roi->bottom - roi->top;
108 const UINT32 halfWidth = (nWidth) / 2;
109 const UINT32 halfHeight = (nHeight) / 2;
110 const UINT32 oddY = 1;
111 const UINT32 evenY = 0;
112 const UINT32 oddX = 1;
113 /* The auxiliary frame is aligned to multiples of 16x16.
114 * We need the padded height for B4 and B5 conversion. */
115 const UINT32 padHeight = nHeight + 16 - nHeight % 16;
116 const BYTE* pSrc[3] = { pSrcRaw[0] + 1ULL * roi->top * srcStep[0] + roi->left,
117 pSrcRaw[1] + 1ULL * roi->top / 2 * srcStep[1] + roi->left / 2,
118 pSrcRaw[2] + 1ULL * roi->top / 2 * srcStep[2] + roi->left / 2 };
119 BYTE* pDst[3] = { pDstRaw[0] + 1ULL * roi->top * dstStep[0] + roi->left,
120 pDstRaw[1] + 1ULL * roi->top * dstStep[1] + roi->left,
121 pDstRaw[2] + 1ULL * roi->top * dstStep[2] + roi->left };
122
123 /* The second half of U and V is a bit more tricky... */
124 /* B4 and B5 */
125 for (size_t y = 0; y < padHeight; y++)
126 {
127 const BYTE* Ya = pSrc[0] + y * srcStep[0];
128 BYTE* pX = nullptr;
129
130 if ((y) % mod < (mod + 1) / 2)
131 {
132 const size_t pos = (2 * uY++ + oddY);
133
134 if (pos >= nHeight)
135 continue;
136
137 pX = pDst[1] + dstStep[1] * pos;
138 }
139 else
140 {
141 const size_t pos = (2 * vY++ + oddY);
142
143 if (pos >= nHeight)
144 continue;
145
146 pX = pDst[2] + dstStep[2] * pos;
147 }
148
149 if (y < nHeight)
150 memcpy(pX, Ya, nWidth);
151 }
152
153 /* B6 and B7 */
154 for (UINT32 y = 0; y < halfHeight; y++)
155 {
156 const UINT32 val2y = (y * 2UL + evenY);
157 const BYTE* Ua = pSrc[1] + 1ULL * y * srcStep[1];
158 const BYTE* Va = pSrc[2] + 1ULL * y * srcStep[2];
159 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * val2y;
160 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * val2y;
161
162 for (UINT32 x = 0; x < halfWidth; x++)
163 {
164 const UINT32 val2x1 = (x * 2 + oddX);
165 pU[val2x1] = Ua[x];
166 pV[val2x1] = Va[x];
167 }
168 }
169
170 return PRIMITIVES_SUCCESS;
171}
172
173static inline pstatus_t general_ChromaV2ToYUV444(const BYTE* WINPR_RESTRICT pSrc[3],
174 const UINT32 srcStep[3], UINT32 nTotalWidth,
175 WINPR_ATTR_UNUSED UINT32 nTotalHeight,
176 BYTE* WINPR_RESTRICT pDst[3],
177 const UINT32 dstStep[3],
178 const RECTANGLE_16* WINPR_RESTRICT roi)
179{
180 const UINT32 nWidth = roi->right - roi->left;
181 const UINT32 nHeight = roi->bottom - roi->top;
182 const UINT32 halfWidth = (nWidth + 1) / 2;
183 const UINT32 halfHeight = (nHeight + 1) / 2;
184 const UINT32 quaterWidth = (nWidth + 3) / 4;
185
186 /* B4 and B5: odd UV values for width/2, height */
187 for (UINT32 y = 0; y < nHeight; y++)
188 {
189 const UINT32 yTop = y + roi->top;
190 const BYTE* pYaU = pSrc[0] + 1ULL * srcStep[0] * yTop + roi->left / 2;
191 const BYTE* pYaV = pYaU + nTotalWidth / 2;
192 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * yTop + roi->left;
193 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * yTop + roi->left;
194
195 for (UINT32 x = 0; x < halfWidth; x++)
196 {
197 const UINT32 odd = 2UL * x + 1UL;
198 pU[odd] = *pYaU++;
199 pV[odd] = *pYaV++;
200 }
201 }
202
203 /* B6 - B9 */
204 for (size_t y = 0; y < halfHeight; y++)
205 {
206 const BYTE* pUaU = pSrc[1] + srcStep[1] * (y + roi->top / 2) + roi->left / 4;
207 const BYTE* pUaV = pUaU + nTotalWidth / 4;
208 const BYTE* pVaU = pSrc[2] + srcStep[2] * (y + roi->top / 2) + roi->left / 4;
209 const BYTE* pVaV = pVaU + nTotalWidth / 4;
210 BYTE* pU = pDst[1] + 1ULL * dstStep[1] * (2ULL * y + 1 + roi->top) + roi->left;
211 BYTE* pV = pDst[2] + 1ULL * dstStep[2] * (2ULL * y + 1 + roi->top) + roi->left;
212
213 for (size_t x = 0; x < quaterWidth; x++)
214 {
215 pU[4 * x + 0] = *pUaU++;
216 pV[4 * x + 0] = *pUaV++;
217 pU[4 * x + 2] = *pVaU++;
218 pV[4 * x + 2] = *pVaV++;
219 }
220 }
221
222 return PRIMITIVES_SUCCESS;
223}
224
225static pstatus_t general_YUV420CombineToYUV444(avc444_frame_type type,
226 const BYTE* WINPR_RESTRICT pSrc[3],
227 const UINT32 srcStep[3], UINT32 nWidth,
228 UINT32 nHeight, BYTE* WINPR_RESTRICT pDst[3],
229 const UINT32 dstStep[3],
230 const RECTANGLE_16* WINPR_RESTRICT roi)
231{
232 if (!pSrc || !pSrc[0] || !pSrc[1] || !pSrc[2])
233 return -1;
234
235 if (!pDst || !pDst[0] || !pDst[1] || !pDst[2])
236 return -1;
237
238 if (!roi)
239 return -1;
240
241 switch (type)
242 {
243 case AVC444_LUMA:
244 return general_LumaToYUV444(pSrc, srcStep, pDst, dstStep, roi);
245
246 case AVC444_CHROMAv1:
247 return general_ChromaV1ToYUV444(pSrc, srcStep, pDst, dstStep, roi);
248
249 case AVC444_CHROMAv2:
250 return general_ChromaV2ToYUV444(pSrc, srcStep, nWidth, nHeight, pDst, dstStep, roi);
251
252 default:
253 return -1;
254 }
255}
256
257static pstatus_t
258general_YUV444SplitToYUV420(const BYTE* WINPR_RESTRICT pSrc[3], const UINT32 srcStep[3],
259 BYTE* WINPR_RESTRICT pMainDst[3], const UINT32 dstMainStep[3],
260 BYTE* WINPR_RESTRICT pAuxDst[3], const UINT32 dstAuxStep[3],
261 const prim_size_t* WINPR_RESTRICT roi)
262{
263 UINT32 uY = 0;
264 UINT32 vY = 0;
265
266 /* The auxiliary frame is aligned to multiples of 16x16.
267 * We need the padded height for B4 and B5 conversion. */
268 const UINT32 padHeight = roi->height + 16 - roi->height % 16;
269 const UINT32 halfWidth = (roi->width + 1) / 2;
270 const UINT32 halfHeight = (roi->height + 1) / 2;
271
272 /* B1 */
273 for (size_t y = 0; y < roi->height; y++)
274 {
275 const BYTE* pSrcY = pSrc[0] + y * srcStep[0];
276 BYTE* pY = pMainDst[0] + y * dstMainStep[0];
277 memcpy(pY, pSrcY, roi->width);
278 }
279
280 /* B2 and B3 */
281 for (size_t y = 0; y < halfHeight; y++)
282 {
283 const BYTE* pSrcU = pSrc[1] + 2ULL * y * srcStep[1];
284 const BYTE* pSrcV = pSrc[2] + 2ULL * y * srcStep[2];
285 BYTE* pU = pMainDst[1] + y * dstMainStep[1];
286 BYTE* pV = pMainDst[2] + y * dstMainStep[2];
287
288 for (size_t x = 0; x < halfWidth; x++)
289 {
290 pU[x] = pSrcV[2 * x];
291 pV[x] = pSrcU[2 * x];
292 }
293 }
294
295 /* B4 and B5 */
296 for (size_t y = 0; y < padHeight; y++)
297 {
298 BYTE* pY = pAuxDst[0] + y * dstAuxStep[0];
299
300 if (y % 16 < 8)
301 {
302 const size_t pos = (2 * uY++ + 1);
303 const BYTE* pSrcU = pSrc[1] + pos * srcStep[1];
304
305 if (pos >= roi->height)
306 continue;
307
308 memcpy(pY, pSrcU, roi->width);
309 }
310 else
311 {
312 const size_t pos = (2 * vY++ + 1);
313 const BYTE* pSrcV = pSrc[2] + pos * srcStep[2];
314
315 if (pos >= roi->height)
316 continue;
317
318 memcpy(pY, pSrcV, roi->width);
319 }
320 }
321
322 /* B6 and B7 */
323 for (size_t y = 0; y < halfHeight; y++)
324 {
325 const BYTE* pSrcU = pSrc[1] + 2 * y * srcStep[1];
326 const BYTE* pSrcV = pSrc[2] + 2 * y * srcStep[2];
327 BYTE* pU = pAuxDst[1] + y * dstAuxStep[1];
328 BYTE* pV = pAuxDst[2] + y * dstAuxStep[2];
329
330 for (size_t x = 0; x < halfWidth; x++)
331 {
332 pU[x] = pSrcU[2 * x + 1];
333 pV[x] = pSrcV[2 * x + 1];
334 }
335 }
336
337 return PRIMITIVES_SUCCESS;
338}
339
340static inline void general_YUV444ToRGB_DOUBLE_ROW(BYTE* WINPR_RESTRICT pRGB[2], UINT32 DstFormat,
341 const BYTE* WINPR_RESTRICT pY[2],
342 const BYTE* WINPR_RESTRICT pU[2],
343 const BYTE* WINPR_RESTRICT pV[2], size_t nWidth)
344{
345 fkt_writePixel writePixel = getPixelWriteFunction(DstFormat, FALSE);
346
347 for (size_t x = 0; x < nWidth; x += 2)
348 {
349 for (size_t i = 0; i < 2; i++)
350 {
351 for (size_t j = 0; j < 2; j++)
352 {
353 const BYTE y = pY[i][x + j];
354 INT32 u = pU[i][x + j];
355 INT32 v = pV[i][x + j];
356 if ((i == 0) && (j == 0))
357 {
358 const INT32 subU = (INT32)pU[0][x + 1] + pU[1][x] + pU[1][x + 1];
359 const INT32 avgU = ((4 * u) - subU);
360 u = CONDITIONAL_CLIP(avgU, WINPR_ASSERTING_INT_CAST(BYTE, u));
361
362 const INT32 subV = (INT32)pV[0][x + 1] + pV[1][x] + pV[1][x + 1];
363 const INT32 avgV = ((4 * v) - subV);
364 v = CONDITIONAL_CLIP(avgV, WINPR_ASSERTING_INT_CAST(BYTE, v));
365 }
366 pRGB[i] = writeYUVPixel(pRGB[i], DstFormat, y, u, v, writePixel);
367 }
368 }
369 }
370}
371
372static inline void general_YUV444ToRGB_SINGLE_ROW(BYTE* WINPR_RESTRICT pRGB, UINT32 DstFormat,
373 const BYTE* WINPR_RESTRICT pY,
374 const BYTE* WINPR_RESTRICT pU,
375 const BYTE* WINPR_RESTRICT pV, size_t nWidth)
376{
377 fkt_writePixel writePixel = getPixelWriteFunction(DstFormat, FALSE);
378
379 for (size_t x = 0; x < nWidth; x += 2)
380 {
381 for (size_t j = 0; j < 2; j++)
382 {
383 const BYTE y = pY[x + j];
384 const BYTE u = pU[x + j];
385 const BYTE v = pV[x + j];
386 pRGB = writeYUVPixel(pRGB, DstFormat, y, u, v, writePixel);
387 }
388 }
389}
390
391static inline pstatus_t general_YUV444ToRGB_8u_P3AC4R_general(const BYTE* WINPR_RESTRICT pSrc[3],
392 const UINT32 srcStep[3],
393 BYTE* WINPR_RESTRICT pDst,
394 UINT32 dstStep, UINT32 DstFormat,
395 const prim_size_t* WINPR_RESTRICT roi)
396{
397 WINPR_ASSERT(pSrc);
398 WINPR_ASSERT(pDst);
399 WINPR_ASSERT(roi);
400
401 const UINT32 nWidth = roi->width;
402 const UINT32 nHeight = roi->height;
403
404 size_t y = 0;
405 for (; y < nHeight - nHeight % 2; y += 2)
406 {
407 const BYTE* WINPR_RESTRICT pY[2] = { pSrc[0] + y * srcStep[0],
408 pSrc[0] + (y + 1) * srcStep[0] };
409 const BYTE* WINPR_RESTRICT pU[2] = { pSrc[1] + y * srcStep[1],
410 pSrc[1] + (y + 1) * srcStep[1] };
411 const BYTE* WINPR_RESTRICT pV[2] = { pSrc[2] + y * srcStep[2],
412 pSrc[2] + (y + 1) * srcStep[2] };
413 BYTE* WINPR_RESTRICT pRGB[] = { pDst + y * dstStep, pDst + (y + 1) * dstStep };
414
415 general_YUV444ToRGB_DOUBLE_ROW(pRGB, DstFormat, pY, pU, pV, nWidth);
416 }
417 for (; y < nHeight; y++)
418 {
419 const BYTE* WINPR_RESTRICT pY = pSrc[0] + y * srcStep[0];
420 const BYTE* WINPR_RESTRICT pU = pSrc[1] + y * srcStep[1];
421 const BYTE* WINPR_RESTRICT pV = pSrc[2] + y * srcStep[2];
422 BYTE* WINPR_RESTRICT pRGB = pDst + y * dstStep;
423
424 general_YUV444ToRGB_SINGLE_ROW(pRGB, DstFormat, pY, pU, pV, nWidth);
425 }
426
427 return PRIMITIVES_SUCCESS;
428}
429
430static inline void general_YUV444ToBGRX_DOUBLE_ROW(BYTE* WINPR_RESTRICT pRGB[2], UINT32 DstFormat,
431 const BYTE* WINPR_RESTRICT pY[2],
432 const BYTE* WINPR_RESTRICT pU[2],
433 const BYTE* WINPR_RESTRICT pV[2], size_t nWidth)
434{
435 WINPR_ASSERT(nWidth % 2 == 0);
436 for (size_t x = 0; x < nWidth; x += 2)
437 {
438 const INT32 subU = pU[0][x + 1] + pU[1][x] + pU[1][x + 1];
439 const INT32 avgU = ((4 * pU[0][x]) - subU);
440 const BYTE useU = CONDITIONAL_CLIP(avgU, pU[0][x]);
441 const INT32 subV = pV[0][x + 1] + pV[1][x] + pV[1][x + 1];
442 const INT32 avgV = ((4 * pV[0][x]) - subV);
443 const BYTE useV = CONDITIONAL_CLIP(avgV, pV[0][x]);
444
445 const BYTE U[2][2] = { { useU, pU[0][x + 1] }, { pU[1][x], pU[1][x + 1] } };
446 const BYTE V[2][2] = { { useV, pV[0][x + 1] }, { pV[1][x], pV[1][x + 1] } };
447
448 for (size_t i = 0; i < 2; i++)
449 {
450 for (size_t j = 0; j < 2; j++)
451 {
452 const BYTE y = pY[i][x + j];
453 const BYTE u = U[i][j];
454 const BYTE v = V[i][j];
455 pRGB[i] = writeYUVPixel(pRGB[i], DstFormat, y, u, v, writePixelBGRX);
456 }
457 }
458 }
459}
460
461static inline void general_YUV444ToBGRX_SINGLE_ROW(BYTE* WINPR_RESTRICT pRGB, UINT32 DstFormat,
462 const BYTE* WINPR_RESTRICT pY,
463 const BYTE* WINPR_RESTRICT pU,
464 const BYTE* WINPR_RESTRICT pV, size_t nWidth)
465{
466 WINPR_ASSERT(nWidth % 2 == 0);
467 for (size_t x = 0; x < nWidth; x += 2)
468 {
469 for (size_t j = 0; j < 2; j++)
470 {
471 const BYTE Y = pY[x + j];
472 const BYTE U = pU[x + j];
473 const BYTE V = pV[x + j];
474 pRGB = writeYUVPixel(pRGB, DstFormat, Y, U, V, writePixelBGRX);
475 }
476 }
477}
478
479static inline pstatus_t general_YUV444ToRGB_8u_P3AC4R_BGRX(const BYTE* WINPR_RESTRICT pSrc[3],
480 const UINT32 srcStep[3],
481 BYTE* WINPR_RESTRICT pDst,
482 UINT32 dstStep, UINT32 DstFormat,
483 const prim_size_t* WINPR_RESTRICT roi)
484{
485 WINPR_ASSERT(pSrc);
486 WINPR_ASSERT(pDst);
487 WINPR_ASSERT(roi);
488
489 const UINT32 nWidth = roi->width;
490 const UINT32 nHeight = roi->height;
491
492 size_t y = 0;
493 for (; y < nHeight - nHeight % 2; y += 2)
494 {
495 const BYTE* pY[2] = { pSrc[0] + y * srcStep[0], pSrc[0] + (y + 1) * srcStep[0] };
496 const BYTE* pU[2] = { pSrc[1] + y * srcStep[1], pSrc[1] + (y + 1) * srcStep[1] };
497 const BYTE* pV[2] = { pSrc[2] + y * srcStep[2], pSrc[2] + (y + 1) * srcStep[2] };
498 BYTE* pRGB[] = { pDst + y * dstStep, pDst + (y + 1) * dstStep };
499
500 general_YUV444ToBGRX_DOUBLE_ROW(pRGB, DstFormat, pY, pU, pV, nWidth);
501 }
502
503 for (; y < nHeight; y++)
504 {
505 const BYTE* WINPR_RESTRICT pY = pSrc[0] + y * srcStep[0];
506 const BYTE* WINPR_RESTRICT pU = pSrc[1] + y * srcStep[1];
507 const BYTE* WINPR_RESTRICT pV = pSrc[2] + y * srcStep[2];
508 BYTE* WINPR_RESTRICT pRGB = pDst + y * dstStep;
509
510 general_YUV444ToBGRX_SINGLE_ROW(pRGB, DstFormat, pY, pU, pV, nWidth);
511 }
512 return PRIMITIVES_SUCCESS;
513}
514
515static pstatus_t general_YUV444ToRGB_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc[3],
516 const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDst,
517 UINT32 dstStep, UINT32 DstFormat,
518 const prim_size_t* WINPR_RESTRICT roi)
519{
520 switch (DstFormat)
521 {
522 case PIXEL_FORMAT_BGRA32:
523 case PIXEL_FORMAT_BGRX32:
524 return general_YUV444ToRGB_8u_P3AC4R_BGRX(pSrc, srcStep, pDst, dstStep, DstFormat, roi);
525
526 default:
527 return general_YUV444ToRGB_8u_P3AC4R_general(pSrc, srcStep, pDst, dstStep, DstFormat,
528 roi);
529 }
530}
536static void general_YUV420ToRGB_8u_P3AC4R_double_line(BYTE* WINPR_RESTRICT pEven,
537 BYTE* WINPR_RESTRICT pOdd, UINT32 DstFormat,
538 const BYTE* WINPR_RESTRICT pYeven,
539 const BYTE* WINPR_RESTRICT pYodd,
540 const BYTE* WINPR_RESTRICT pU,
541 const BYTE* WINPR_RESTRICT pV, UINT32 width,
542 fkt_writePixel writePixel, UINT32 formatSize)
543{
544
545 UINT32 x = 0;
546 for (; x < width / 2; x++)
547 {
548 const BYTE U = pU[x];
549 const BYTE V = pV[x];
550 const BYTE eY0 = pYeven[2ULL * x + 0];
551 const BYTE eY1 = pYeven[2ULL * x + 1];
552 writeYUVPixel(&pEven[2ULL * x * formatSize], DstFormat, eY0, U, V, writePixel);
553 writeYUVPixel(&pEven[(2ULL * x + 1) * formatSize], DstFormat, eY1, U, V, writePixel);
554
555 const BYTE oY0 = pYodd[2ULL * x + 0];
556 const BYTE oY1 = pYodd[2ULL * x + 1];
557 writeYUVPixel(&pOdd[2ULL * x * formatSize], DstFormat, oY0, U, V, writePixel);
558 writeYUVPixel(&pOdd[(2ULL * x + 1) * formatSize], DstFormat, oY1, U, V, writePixel);
559 }
560
561 for (; x < (width + 1) / 2; x++)
562 {
563 const BYTE U = pU[x];
564 const BYTE V = pV[x];
565 const BYTE eY0 = pYeven[2ULL * x + 0];
566 writeYUVPixel(&pEven[2ULL * x * formatSize], DstFormat, eY0, U, V, writePixel);
567
568 const BYTE oY0 = pYodd[2ULL * x + 0];
569 writeYUVPixel(&pOdd[2ULL * x * formatSize], DstFormat, oY0, U, V, writePixel);
570 }
571}
572
573static void general_YUV420ToRGB_8u_P3AC4R_single_line(BYTE* WINPR_RESTRICT pEven, UINT32 DstFormat,
574 const BYTE* WINPR_RESTRICT pYeven,
575 const BYTE* WINPR_RESTRICT pU,
576 const BYTE* WINPR_RESTRICT pV, UINT32 width,
577 fkt_writePixel writePixel, UINT32 formatSize)
578{
579
580 UINT32 x = 0;
581 for (; x < width / 2; x++)
582 {
583 const BYTE U = pU[x];
584 const BYTE V = pV[x];
585 const BYTE eY0 = pYeven[2ULL * x + 0];
586 const BYTE eY1 = pYeven[2ULL * x + 1];
587 writeYUVPixel(&pEven[2ULL * x * formatSize], DstFormat, eY0, U, V, writePixel);
588 writeYUVPixel(&pEven[(2ULL * x + 1) * formatSize], DstFormat, eY1, U, V, writePixel);
589 }
590
591 for (; x < (width + 1) / 2; x++)
592 {
593 const BYTE U = pU[x];
594 const BYTE V = pV[x];
595 const BYTE eY0 = pYeven[2ULL * x + 0];
596 writeYUVPixel(&pEven[2ULL * x * formatSize], DstFormat, eY0, U, V, writePixel);
597 }
598}
599
600static pstatus_t general_YUV420ToRGB_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc[3],
601 const UINT32 srcStep[3], BYTE* WINPR_RESTRICT pDst,
602 UINT32 dstStep, UINT32 DstFormat,
603 const prim_size_t* WINPR_RESTRICT roi)
604{
605 WINPR_ASSERT(roi);
606 const DWORD formatSize = FreeRDPGetBytesPerPixel(DstFormat);
607 fkt_writePixel writePixel = getPixelWriteFunction(DstFormat, FALSE);
608 const UINT32 nWidth = roi->width;
609 const UINT32 nHeight = roi->height;
610
611 UINT32 y = 0;
612 for (; y < nHeight / 2; y++)
613 {
614 const BYTE* pYe = &pSrc[0][(2ULL * y + 0) * srcStep[0]];
615 const BYTE* pYo = &pSrc[0][(2ULL * y + 1) * srcStep[0]];
616 const BYTE* pU = &pSrc[1][1ULL * srcStep[1] * y];
617 const BYTE* pV = &pSrc[2][1ULL * srcStep[2] * y];
618 BYTE* pRGBeven = &pDst[2ULL * y * dstStep];
619 BYTE* pRGBodd = &pDst[(2ULL * y + 1) * dstStep];
620 general_YUV420ToRGB_8u_P3AC4R_double_line(pRGBeven, pRGBodd, DstFormat, pYe, pYo, pU, pV,
621 nWidth, writePixel, formatSize);
622 }
623
624 // Last row (if odd)
625 for (; y < (nHeight + 1) / 2; y++)
626 {
627 const BYTE* pY = &pSrc[0][2ULL * srcStep[0] * y];
628 const BYTE* pU = &pSrc[1][1ULL * srcStep[1] * y];
629 const BYTE* pV = &pSrc[2][1ULL * srcStep[2] * y];
630 BYTE* pEven = &pDst[2ULL * y * dstStep];
631
632 general_YUV420ToRGB_8u_P3AC4R_single_line(pEven, DstFormat, pY, pU, pV, nWidth, writePixel,
633 formatSize);
634 }
635
636 return PRIMITIVES_SUCCESS;
637}
638
639static inline void BGRX_fillYUV(size_t offset, const BYTE* WINPR_RESTRICT pRGB[2],
640 BYTE* WINPR_RESTRICT pY[2], BYTE* WINPR_RESTRICT pU[2],
641 BYTE* WINPR_RESTRICT pV[2])
642{
643 WINPR_ASSERT(pRGB);
644 WINPR_ASSERT(pY);
645 WINPR_ASSERT(pU);
646 WINPR_ASSERT(pV);
647
648 const UINT32 SrcFormat = PIXEL_FORMAT_BGRX32;
649 const UINT32 bpp = 4;
650
651 for (size_t i = 0; i < 2; i++)
652 {
653 for (size_t j = 0; j < 2; j++)
654 {
655 BYTE B = 0;
656 BYTE G = 0;
657 BYTE R = 0;
658 const UINT32 color = FreeRDPReadColor(&pRGB[i][(offset + j) * bpp], SrcFormat);
659 FreeRDPSplitColor(color, SrcFormat, &R, &G, &B, nullptr, nullptr);
660 pY[i][offset + j] = RGB2Y(R, G, B);
661 pU[i][offset + j] = RGB2U(R, G, B);
662 pV[i][offset + j] = RGB2V(R, G, B);
663 }
664 }
665
666 /* Apply chroma filter */
667 const INT32 avgU = (pU[0][offset] + pU[0][offset + 1] + pU[1][offset] + pU[1][offset + 1]) / 4;
668 pU[0][offset] = CONDITIONAL_CLIP(avgU, pU[0][offset]);
669 const INT32 avgV = (pV[0][offset] + pV[0][offset + 1] + pV[1][offset] + pV[1][offset + 1]) / 4;
670 pV[0][offset] = CONDITIONAL_CLIP(avgV, pV[0][offset]);
671}
672
673static inline void BGRX_fillYUV_single(size_t offset, const BYTE* WINPR_RESTRICT pRGB,
674 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
675 BYTE* WINPR_RESTRICT pV)
676{
677 WINPR_ASSERT(pRGB);
678 WINPR_ASSERT(pY);
679 WINPR_ASSERT(pU);
680 WINPR_ASSERT(pV);
681
682 const UINT32 SrcFormat = PIXEL_FORMAT_BGRX32;
683 const UINT32 bpp = 4;
684
685 for (size_t j = 0; j < 2; j++)
686 {
687 BYTE B = 0;
688 BYTE G = 0;
689 BYTE R = 0;
690 const UINT32 color = FreeRDPReadColor(&pRGB[(offset + j) * bpp], SrcFormat);
691 FreeRDPSplitColor(color, SrcFormat, &R, &G, &B, nullptr, nullptr);
692 pY[offset + j] = RGB2Y(R, G, B);
693 pU[offset + j] = RGB2U(R, G, B);
694 pV[offset + j] = RGB2V(R, G, B);
695 }
696}
697
698static inline void general_BGRXToYUV444_DOUBLE_ROW(const BYTE* WINPR_RESTRICT pRGB[2],
699 BYTE* WINPR_RESTRICT pY[2],
700 BYTE* WINPR_RESTRICT pU[2],
701 BYTE* WINPR_RESTRICT pV[2], UINT32 nWidth)
702{
703 for (size_t x = 0; x < nWidth; x += 2)
704 {
705 BGRX_fillYUV(x, pRGB, pY, pU, pV);
706 }
707}
708
709static inline void general_BGRXToYUV444_SINGLE_ROW(const BYTE* WINPR_RESTRICT pRGB,
710 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
711 BYTE* WINPR_RESTRICT pV, UINT32 nWidth)
712{
713 for (size_t x = 0; x < nWidth; x += 2)
714 {
715 BGRX_fillYUV_single(x, pRGB, pY, pU, pV);
716 }
717}
718
719static inline pstatus_t general_RGBToYUV444_8u_P3AC4R_BGRX(const BYTE* WINPR_RESTRICT pSrc,
720 const UINT32 srcStep,
721 BYTE* WINPR_RESTRICT pDst[3],
722 const UINT32 dstStep[3],
723 const prim_size_t* WINPR_RESTRICT roi)
724{
725 const UINT32 nWidth = roi->width;
726 const UINT32 nHeight = roi->height;
727
728 size_t y = 0;
729 for (; y < nHeight - nHeight % 2; y += 2)
730 {
731 const BYTE* pRGB[] = { pSrc + y * srcStep, pSrc + (y + 1) * srcStep };
732 BYTE* pY[] = { pDst[0] + y * dstStep[0], pDst[0] + (y + 1) * dstStep[0] };
733 BYTE* pU[] = { pDst[1] + y * dstStep[1], pDst[1] + (y + 1) * dstStep[1] };
734 BYTE* pV[] = { pDst[2] + y * dstStep[2], pDst[2] + (y + 1) * dstStep[2] };
735
736 general_BGRXToYUV444_DOUBLE_ROW(pRGB, pY, pU, pV, nWidth);
737 }
738
739 for (; y < nHeight; y++)
740 {
741 const BYTE* pRGB = pSrc + y * srcStep;
742 BYTE* pY = pDst[0] + y * dstStep[0];
743 BYTE* pU = pDst[1] + y * dstStep[1];
744 BYTE* pV = pDst[2] + y * dstStep[2];
745
746 general_BGRXToYUV444_SINGLE_ROW(pRGB, pY, pU, pV, nWidth);
747 }
748
749 return PRIMITIVES_SUCCESS;
750}
751
752static inline void fillYUV(size_t offset, const BYTE* WINPR_RESTRICT pRGB[2], UINT32 SrcFormat,
753 BYTE* WINPR_RESTRICT pY[2], BYTE* WINPR_RESTRICT pU[2],
754 BYTE* WINPR_RESTRICT pV[2])
755{
756 WINPR_ASSERT(pRGB);
757 WINPR_ASSERT(pY);
758 WINPR_ASSERT(pU);
759 WINPR_ASSERT(pV);
760 const UINT32 bpp = FreeRDPGetBytesPerPixel(SrcFormat);
761
762 INT32 avgU = 0;
763 INT32 avgV = 0;
764 for (size_t i = 0; i < 2; i++)
765 {
766 for (size_t j = 0; j < 2; j++)
767 {
768 BYTE B = 0;
769 BYTE G = 0;
770 BYTE R = 0;
771 const UINT32 color = FreeRDPReadColor(&pRGB[i][(offset + j) * bpp], SrcFormat);
772 FreeRDPSplitColor(color, SrcFormat, &R, &G, &B, nullptr, nullptr);
773 const BYTE y = RGB2Y(R, G, B);
774 const BYTE u = RGB2U(R, G, B);
775 const BYTE v = RGB2V(R, G, B);
776 avgU += u;
777 avgV += v;
778 pY[i][offset + j] = y;
779 pU[i][offset + j] = u;
780 pV[i][offset + j] = v;
781 }
782 }
783
784 /* Apply chroma filter */
785 avgU /= 4;
786 pU[0][offset] = CLIP(avgU);
787
788 avgV /= 4;
789 pV[0][offset] = CLIP(avgV);
790}
791
792static inline void fillYUV_single(size_t offset, const BYTE* WINPR_RESTRICT pRGB, UINT32 SrcFormat,
793 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
794 BYTE* WINPR_RESTRICT pV)
795{
796 WINPR_ASSERT(pRGB);
797 WINPR_ASSERT(pY);
798 WINPR_ASSERT(pU);
799 WINPR_ASSERT(pV);
800 const UINT32 bpp = FreeRDPGetBytesPerPixel(SrcFormat);
801
802 for (size_t j = 0; j < 2; j++)
803 {
804 BYTE B = 0;
805 BYTE G = 0;
806 BYTE R = 0;
807 const UINT32 color = FreeRDPReadColor(&pRGB[(offset + j) * bpp], SrcFormat);
808 FreeRDPSplitColor(color, SrcFormat, &R, &G, &B, nullptr, nullptr);
809 const BYTE y = RGB2Y(R, G, B);
810 const BYTE u = RGB2U(R, G, B);
811 const BYTE v = RGB2V(R, G, B);
812 pY[offset + j] = y;
813 pU[offset + j] = u;
814 pV[offset + j] = v;
815 }
816}
817
818static inline void general_RGBToYUV444_DOUBLE_ROW(const BYTE* WINPR_RESTRICT pRGB[2],
819 UINT32 SrcFormat, BYTE* WINPR_RESTRICT pY[2],
820 BYTE* WINPR_RESTRICT pU[2],
821 BYTE* WINPR_RESTRICT pV[2], UINT32 nWidth)
822{
823 for (size_t x = 0; x < nWidth; x += 2)
824 {
825 fillYUV(x, pRGB, SrcFormat, pY, pU, pV);
826 }
827}
828
829static inline void general_RGBToYUV444_SINGLE_ROW(const BYTE* WINPR_RESTRICT pRGB, UINT32 SrcFormat,
830 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
831 BYTE* WINPR_RESTRICT pV, UINT32 nWidth)
832{
833 for (size_t x = 0; x < nWidth; x += 2)
834 {
835 fillYUV_single(x, pRGB, SrcFormat, pY, pU, pV);
836 }
837}
838
839static inline pstatus_t general_RGBToYUV444_8u_P3AC4R_RGB(const BYTE* WINPR_RESTRICT pSrc,
840 UINT32 SrcFormat, const UINT32 srcStep,
841 BYTE* WINPR_RESTRICT pDst[3],
842 const UINT32 dstStep[3],
843 const prim_size_t* WINPR_RESTRICT roi)
844{
845 const UINT32 nWidth = roi->width;
846 const UINT32 nHeight = roi->height;
847
848 size_t y = 0;
849 for (; y < nHeight - nHeight % 2; y += 2)
850 {
851 const BYTE* pRGB[] = { pSrc + y * srcStep, pSrc + (y + 1) * srcStep };
852 BYTE* pY[] = { &pDst[0][y * dstStep[0]], &pDst[0][(y + 1) * dstStep[0]] };
853 BYTE* pU[] = { &pDst[1][y * dstStep[1]], &pDst[1][(y + 1) * dstStep[1]] };
854 BYTE* pV[] = { &pDst[2][y * dstStep[2]], &pDst[2][(y + 1) * dstStep[2]] };
855
856 general_RGBToYUV444_DOUBLE_ROW(pRGB, SrcFormat, pY, pU, pV, nWidth);
857 }
858 for (; y < nHeight; y++)
859 {
860 const BYTE* pRGB = pSrc + y * srcStep;
861 BYTE* pY = &pDst[0][y * dstStep[0]];
862 BYTE* pU = &pDst[1][y * dstStep[1]];
863 BYTE* pV = &pDst[2][y * dstStep[2]];
864
865 general_RGBToYUV444_SINGLE_ROW(pRGB, SrcFormat, pY, pU, pV, nWidth);
866 }
867
868 return PRIMITIVES_SUCCESS;
869}
870
871static pstatus_t general_RGBToYUV444_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc, UINT32 SrcFormat,
872 const UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
873 const UINT32 dstStep[3],
874 const prim_size_t* WINPR_RESTRICT roi)
875{
876 switch (SrcFormat)
877 {
878 case PIXEL_FORMAT_BGRA32:
879 case PIXEL_FORMAT_BGRX32:
880 return general_RGBToYUV444_8u_P3AC4R_BGRX(pSrc, srcStep, pDst, dstStep, roi);
881 default:
882 return general_RGBToYUV444_8u_P3AC4R_RGB(pSrc, SrcFormat, srcStep, pDst, dstStep, roi);
883 }
884}
885
886static inline void fillI444_single(size_t offset, const BYTE* WINPR_RESTRICT pRGB, UINT32 SrcFormat,
887 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
888 BYTE* WINPR_RESTRICT pV)
889{
890 WINPR_ASSERT(pRGB);
891 WINPR_ASSERT(pY);
892 WINPR_ASSERT(pU);
893 WINPR_ASSERT(pV);
894
895 const UINT32 bpp = FreeRDPGetBytesPerPixel(SrcFormat);
896
897 BYTE B = 0;
898 BYTE G = 0;
899 BYTE R = 0;
900 const UINT32 color = FreeRDPReadColor(&pRGB[offset * bpp], SrcFormat);
901 FreeRDPSplitColor(color, SrcFormat, &R, &G, &B, nullptr, nullptr);
902 const BYTE y = RGB2Y(R, G, B);
903 const BYTE u = RGB2U(R, G, B);
904 const BYTE v = RGB2V(R, G, B);
905 pY[offset] = y;
906 pU[offset] = u;
907 pV[offset] = v;
908}
909
910static inline void general_RGBToI444_SINGLE_ROW(const BYTE* WINPR_RESTRICT pRGB, UINT32 SrcFormat,
911 BYTE* WINPR_RESTRICT pY, BYTE* WINPR_RESTRICT pU,
912 BYTE* WINPR_RESTRICT pV, UINT32 nWidth)
913{
914 for (size_t x = 0; x < nWidth; x++)
915 {
916 fillI444_single(x, pRGB, SrcFormat, pY, pU, pV);
917 }
918}
919
920static inline pstatus_t general_RGBToI444_8u_RGB(const BYTE* WINPR_RESTRICT pSrc, UINT32 SrcFormat,
921 const UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
922 const UINT32 dstStep[3],
923 const prim_size_t* WINPR_RESTRICT roi)
924{
925 const UINT32 nWidth = roi->width;
926 const UINT32 nHeight = roi->height;
927
928 for (size_t y = 0; y < nHeight; y++)
929 {
930 const BYTE* pRGB = pSrc + y * srcStep;
931 BYTE* pY = &pDst[0][y * dstStep[0]];
932 BYTE* pU = &pDst[1][y * dstStep[1]];
933 BYTE* pV = &pDst[2][y * dstStep[2]];
934
935 general_RGBToI444_SINGLE_ROW(pRGB, SrcFormat, pY, pU, pV, nWidth);
936 }
937
938 return PRIMITIVES_SUCCESS;
939}
940
941static pstatus_t general_RGBToI444_8u(const BYTE* WINPR_RESTRICT pSrc, UINT32 SrcFormat,
942 const UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
943 const UINT32 dstStep[3],
944 const prim_size_t* WINPR_RESTRICT roi)
945{
946 switch (SrcFormat)
947 {
948 default:
949 return general_RGBToI444_8u_RGB(pSrc, SrcFormat, srcStep, pDst, dstStep, roi);
950 }
951}
952
953static inline pstatus_t general_RGBToYUV420_BGRX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
954 BYTE* WINPR_RESTRICT pDst[3],
955 const UINT32 dstStep[3],
956 const prim_size_t* WINPR_RESTRICT roi)
957{
958 size_t x1 = 0;
959 size_t x2 = 4;
960 size_t x3 = srcStep;
961 size_t x4 = srcStep + 4;
962 size_t y1 = 0;
963 size_t y2 = 1;
964 size_t y3 = dstStep[0];
965 size_t y4 = dstStep[0] + 1;
966 UINT32 max_x = roi->width - 1;
967
968 size_t y = 0;
969 for (size_t i = 0; y < roi->height - roi->height % 2; y += 2, i++)
970 {
971 const BYTE* src = pSrc + y * srcStep;
972 BYTE* ydst = pDst[0] + y * dstStep[0];
973 BYTE* udst = pDst[1] + i * dstStep[1];
974 BYTE* vdst = pDst[2] + i * dstStep[2];
975
976 for (size_t x = 0; x < roi->width; x += 2)
977 {
978 BYTE R = 0;
979 BYTE G = 0;
980 BYTE B = 0;
981 INT32 Ra = 0;
982 INT32 Ga = 0;
983 INT32 Ba = 0;
984 /* row 1, pixel 1 */
985 Ba = B = *(src + x1 + 0);
986 Ga = G = *(src + x1 + 1);
987 Ra = R = *(src + x1 + 2);
988 ydst[y1] = RGB2Y(R, G, B);
989
990 if (x < max_x)
991 {
992 /* row 1, pixel 2 */
993 Ba += B = *(src + x2 + 0);
994 Ga += G = *(src + x2 + 1);
995 Ra += R = *(src + x2 + 2);
996 ydst[y2] = RGB2Y(R, G, B);
997 }
998
999 /* row 2, pixel 1 */
1000 Ba += B = *(src + x3 + 0);
1001 Ga += G = *(src + x3 + 1);
1002 Ra += R = *(src + x3 + 2);
1003 ydst[y3] = RGB2Y(R, G, B);
1004
1005 if (x < max_x)
1006 {
1007 /* row 2, pixel 2 */
1008 Ba += B = *(src + x4 + 0);
1009 Ga += G = *(src + x4 + 1);
1010 Ra += R = *(src + x4 + 2);
1011 ydst[y4] = RGB2Y(R, G, B);
1012 }
1013
1014 Ba >>= 2;
1015 Ga >>= 2;
1016 Ra >>= 2;
1017 *udst++ = RGB2U(Ra, Ga, Ba);
1018 *vdst++ = RGB2V(Ra, Ga, Ba);
1019 ydst += 2;
1020 src += 8;
1021 }
1022 }
1023
1024 for (; y < roi->height; y++)
1025 {
1026 const BYTE* src = pSrc + y * srcStep;
1027 BYTE* ydst = pDst[0] + y * dstStep[0];
1028 BYTE* udst = pDst[1] + (y / 2) * dstStep[1];
1029 BYTE* vdst = pDst[2] + (y / 2) * dstStep[2];
1030
1031 for (size_t x = 0; x < roi->width; x += 2)
1032 {
1033 BYTE R = 0;
1034 BYTE G = 0;
1035 BYTE B = 0;
1036 INT32 Ra = 0;
1037 INT32 Ga = 0;
1038 INT32 Ba = 0;
1039 /* row 1, pixel 1 */
1040 Ba = B = *(src + x1 + 0);
1041 Ga = G = *(src + x1 + 1);
1042 Ra = R = *(src + x1 + 2);
1043 ydst[y1] = RGB2Y(R, G, B);
1044
1045 if (x < max_x)
1046 {
1047 /* row 1, pixel 2 */
1048 Ba += B = *(src + x2 + 0);
1049 Ga += G = *(src + x2 + 1);
1050 Ra += R = *(src + x2 + 2);
1051 ydst[y2] = RGB2Y(R, G, B);
1052 }
1053
1054 Ba >>= 2;
1055 Ga >>= 2;
1056 Ra >>= 2;
1057 *udst++ = RGB2U(Ra, Ga, Ba);
1058 *vdst++ = RGB2V(Ra, Ga, Ba);
1059 ydst += 2;
1060 src += 8;
1061 }
1062 }
1063
1064 return PRIMITIVES_SUCCESS;
1065}
1066
1067static inline pstatus_t general_RGBToYUV420_RGBX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
1068 BYTE* WINPR_RESTRICT pDst[3],
1069 const UINT32 dstStep[3],
1070 const prim_size_t* WINPR_RESTRICT roi)
1071{
1072 size_t x1 = 0;
1073 size_t x2 = 4;
1074 size_t x3 = srcStep;
1075 size_t x4 = srcStep + 4;
1076 size_t y1 = 0;
1077 size_t y2 = 1;
1078 size_t y3 = dstStep[0];
1079 size_t y4 = dstStep[0] + 1;
1080 UINT32 max_x = roi->width - 1;
1081
1082 size_t y = 0;
1083 for (size_t i = 0; y < roi->height - roi->height % 2; y += 2, i++)
1084 {
1085 const BYTE* src = pSrc + y * srcStep;
1086 BYTE* ydst = pDst[0] + y * dstStep[0];
1087 BYTE* udst = pDst[1] + i * dstStep[1];
1088 BYTE* vdst = pDst[2] + i * dstStep[2];
1089
1090 for (UINT32 x = 0; x < roi->width; x += 2)
1091 {
1092 BYTE R = *(src + x1 + 0);
1093 BYTE G = *(src + x1 + 1);
1094 BYTE B = *(src + x1 + 2);
1095 /* row 1, pixel 1 */
1096 INT32 Ra = R;
1097 INT32 Ga = G;
1098 INT32 Ba = B;
1099 ydst[y1] = RGB2Y(R, G, B);
1100
1101 if (x < max_x)
1102 {
1103 /* row 1, pixel 2 */
1104 R = *(src + x2 + 0);
1105 G = *(src + x2 + 1);
1106 B = *(src + x2 + 2);
1107 Ra += R;
1108 Ga += G;
1109 Ba += B;
1110 ydst[y2] = RGB2Y(R, G, B);
1111 }
1112
1113 /* row 2, pixel 1 */
1114 R = *(src + x3 + 0);
1115 G = *(src + x3 + 1);
1116 B = *(src + x3 + 2);
1117
1118 Ra += R;
1119 Ga += G;
1120 Ba += B;
1121 ydst[y3] = RGB2Y(R, G, B);
1122
1123 if (x < max_x)
1124 {
1125 /* row 2, pixel 2 */
1126 R = *(src + x4 + 0);
1127 G = *(src + x4 + 1);
1128 B = *(src + x4 + 2);
1129
1130 Ra += R;
1131 Ga += G;
1132 Ba += B;
1133 ydst[y4] = RGB2Y(R, G, B);
1134 }
1135
1136 Ba >>= 2;
1137 Ga >>= 2;
1138 Ra >>= 2;
1139 *udst++ = RGB2U(Ra, Ga, Ba);
1140 *vdst++ = RGB2V(Ra, Ga, Ba);
1141 ydst += 2;
1142 src += 8;
1143 }
1144 }
1145
1146 for (; y < roi->height; y++)
1147 {
1148 const BYTE* src = pSrc + y * srcStep;
1149 BYTE* ydst = pDst[0] + y * dstStep[0];
1150 BYTE* udst = pDst[1] + (y / 2) * dstStep[1];
1151 BYTE* vdst = pDst[2] + (y / 2) * dstStep[2];
1152
1153 for (UINT32 x = 0; x < roi->width; x += 2)
1154 {
1155 BYTE R = *(src + x1 + 0);
1156 BYTE G = *(src + x1 + 1);
1157 BYTE B = *(src + x1 + 2);
1158 /* row 1, pixel 1 */
1159 INT32 Ra = R;
1160 INT32 Ga = G;
1161 INT32 Ba = B;
1162 ydst[y1] = RGB2Y(R, G, B);
1163
1164 if (x < max_x)
1165 {
1166 /* row 1, pixel 2 */
1167 R = *(src + x2 + 0);
1168 G = *(src + x2 + 1);
1169 B = *(src + x2 + 2);
1170 Ra += R;
1171 Ga += G;
1172 Ba += B;
1173 ydst[y2] = RGB2Y(R, G, B);
1174 }
1175
1176 Ba >>= 2;
1177 Ga >>= 2;
1178 Ra >>= 2;
1179 *udst++ = RGB2U(Ra, Ga, Ba);
1180 *vdst++ = RGB2V(Ra, Ga, Ba);
1181 ydst += 2;
1182 src += 8;
1183 }
1184 }
1185
1186 return PRIMITIVES_SUCCESS;
1187}
1188
1189static inline pstatus_t general_RGBToYUV420_ANY(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1190 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
1191 const UINT32 dstStep[3],
1192 const prim_size_t* WINPR_RESTRICT roi)
1193{
1194 const UINT32 bpp = FreeRDPGetBytesPerPixel(srcFormat);
1195 size_t x1 = 0;
1196 size_t x2 = bpp;
1197 size_t x3 = srcStep;
1198 size_t x4 = srcStep + bpp;
1199 size_t y1 = 0;
1200 size_t y2 = 1;
1201 size_t y3 = dstStep[0];
1202 size_t y4 = dstStep[0] + 1;
1203 UINT32 max_x = roi->width - 1;
1204
1205 size_t y = 0;
1206 for (size_t i = 0; y < roi->height - roi->height % 2; y += 2, i++)
1207 {
1208 const BYTE* src = pSrc + y * srcStep;
1209 BYTE* ydst = pDst[0] + y * dstStep[0];
1210 BYTE* udst = pDst[1] + i * dstStep[1];
1211 BYTE* vdst = pDst[2] + i * dstStep[2];
1212
1213 for (size_t x = 0; x < roi->width; x += 2)
1214 {
1215 BYTE R = 0;
1216 BYTE G = 0;
1217 BYTE B = 0;
1218 INT32 Ra = 0;
1219 INT32 Ga = 0;
1220 INT32 Ba = 0;
1221 UINT32 color = 0;
1222 /* row 1, pixel 1 */
1223 color = FreeRDPReadColor(src + x1, srcFormat);
1224 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1225 Ra = R;
1226 Ga = G;
1227 Ba = B;
1228 ydst[y1] = RGB2Y(R, G, B);
1229
1230 if (x < max_x)
1231 {
1232 /* row 1, pixel 2 */
1233 color = FreeRDPReadColor(src + x2, srcFormat);
1234 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1235 Ra += R;
1236 Ga += G;
1237 Ba += B;
1238 ydst[y2] = RGB2Y(R, G, B);
1239 }
1240
1241 /* row 2, pixel 1 */
1242 color = FreeRDPReadColor(src + x3, srcFormat);
1243 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1244 Ra += R;
1245 Ga += G;
1246 Ba += B;
1247 ydst[y3] = RGB2Y(R, G, B);
1248
1249 if (x < max_x)
1250 {
1251 /* row 2, pixel 2 */
1252 color = FreeRDPReadColor(src + x4, srcFormat);
1253 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1254 Ra += R;
1255 Ga += G;
1256 Ba += B;
1257 ydst[y4] = RGB2Y(R, G, B);
1258 }
1259
1260 Ra >>= 2;
1261 Ga >>= 2;
1262 Ba >>= 2;
1263 *udst++ = RGB2U(Ra, Ga, Ba);
1264 *vdst++ = RGB2V(Ra, Ga, Ba);
1265 ydst += 2;
1266 src += 2ULL * bpp;
1267 }
1268 }
1269
1270 for (; y < roi->height; y++)
1271 {
1272 const BYTE* src = pSrc + y * srcStep;
1273 BYTE* ydst = pDst[0] + y * dstStep[0];
1274 BYTE* udst = pDst[1] + (y / 2) * dstStep[1];
1275 BYTE* vdst = pDst[2] + (y / 2) * dstStep[2];
1276
1277 for (size_t x = 0; x < roi->width; x += 2)
1278 {
1279 BYTE R = 0;
1280 BYTE G = 0;
1281 BYTE B = 0;
1282 /* row 1, pixel 1 */
1283 UINT32 color = FreeRDPReadColor(src + x1, srcFormat);
1284 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1285 INT32 Ra = R;
1286 INT32 Ga = G;
1287 INT32 Ba = B;
1288 ydst[y1] = RGB2Y(R, G, B);
1289
1290 if (x < max_x)
1291 {
1292 /* row 1, pixel 2 */
1293 color = FreeRDPReadColor(src + x2, srcFormat);
1294 FreeRDPSplitColor(color, srcFormat, &R, &G, &B, nullptr, nullptr);
1295 Ra += R;
1296 Ga += G;
1297 Ba += B;
1298 ydst[y2] = RGB2Y(R, G, B);
1299 }
1300
1301 Ra >>= 2;
1302 Ga >>= 2;
1303 Ba >>= 2;
1304 *udst++ = RGB2U(Ra, Ga, Ba);
1305 *vdst++ = RGB2V(Ra, Ga, Ba);
1306 ydst += 2;
1307 src += 2ULL * bpp;
1308 }
1309 }
1310
1311 return PRIMITIVES_SUCCESS;
1312}
1313
1314static pstatus_t general_RGBToYUV420_8u_P3AC4R(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1315 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst[3],
1316 const UINT32 dstStep[3],
1317 const prim_size_t* WINPR_RESTRICT roi)
1318{
1319 switch (srcFormat)
1320 {
1321 case PIXEL_FORMAT_BGRA32:
1322 case PIXEL_FORMAT_BGRX32:
1323 return general_RGBToYUV420_BGRX(pSrc, srcStep, pDst, dstStep, roi);
1324
1325 case PIXEL_FORMAT_RGBA32:
1326 case PIXEL_FORMAT_RGBX32:
1327 return general_RGBToYUV420_RGBX(pSrc, srcStep, pDst, dstStep, roi);
1328
1329 default:
1330 return general_RGBToYUV420_ANY(pSrc, srcFormat, srcStep, pDst, dstStep, roi);
1331 }
1332}
1333
1334static inline void int_general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(
1335 size_t offset, const BYTE* WINPR_RESTRICT pSrcEven, const BYTE* WINPR_RESTRICT pSrcOdd,
1336 BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
1337 BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1338 BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
1339{
1340 for (size_t x = offset; x < width; x += 2)
1341 {
1342 const BYTE* srcEven = &pSrcEven[4ULL * x];
1343 const BYTE* srcOdd = &pSrcOdd[4ULL * x];
1344 const BOOL lastX = (x + 1) >= width;
1345 BYTE Y1e = 0;
1346 BYTE Y2e = 0;
1347 BYTE U1e = 0;
1348 BYTE V1e = 0;
1349 BYTE U2e = 0;
1350 BYTE V2e = 0;
1351 BYTE Y1o = 0;
1352 BYTE Y2o = 0;
1353 BYTE U1o = 0;
1354 BYTE V1o = 0;
1355 BYTE U2o = 0;
1356 BYTE V2o = 0;
1357 /* Read 4 pixels, 2 from even, 2 from odd lines */
1358 {
1359 const BYTE b = *srcEven++;
1360 const BYTE g = *srcEven++;
1361 const BYTE r = *srcEven++;
1362 srcEven++;
1363 Y1e = Y2e = Y1o = Y2o = RGB2Y(r, g, b);
1364 U1e = U2e = U1o = U2o = RGB2U(r, g, b);
1365 V1e = V2e = V1o = V2o = RGB2V(r, g, b);
1366 }
1367
1368 if (!lastX)
1369 {
1370 const BYTE b = *srcEven++;
1371 const BYTE g = *srcEven++;
1372 const BYTE r = *srcEven++;
1373 srcEven++;
1374 Y2e = RGB2Y(r, g, b);
1375 U2e = RGB2U(r, g, b);
1376 V2e = RGB2V(r, g, b);
1377 }
1378
1379 if (b1Odd)
1380 {
1381 const BYTE b = *srcOdd++;
1382 const BYTE g = *srcOdd++;
1383 const BYTE r = *srcOdd++;
1384 srcOdd++;
1385 Y1o = Y2o = RGB2Y(r, g, b);
1386 U1o = U2o = RGB2U(r, g, b);
1387 V1o = V2o = RGB2V(r, g, b);
1388 }
1389
1390 if (b1Odd && !lastX)
1391 {
1392 const BYTE b = *srcOdd++;
1393 const BYTE g = *srcOdd++;
1394 const BYTE r = *srcOdd++;
1395 srcOdd++;
1396 Y2o = RGB2Y(r, g, b);
1397 U2o = RGB2U(r, g, b);
1398 V2o = RGB2V(r, g, b);
1399 }
1400
1401 /* We have 4 Y pixels, so store them. */
1402 *b1Even++ = Y1e;
1403 *b1Even++ = Y2e;
1404
1405 if (b1Odd)
1406 {
1407 *b1Odd++ = Y1o;
1408 *b1Odd++ = Y2o;
1409 }
1410
1411 /* 2x 2y pixel in luma UV plane use averaging
1412 */
1413 {
1414 const BYTE Uavg = WINPR_ASSERTING_INT_CAST(BYTE, ((UINT16)U1e + U2e + U1o + U2o) / 4);
1415 const BYTE Vavg = WINPR_ASSERTING_INT_CAST(BYTE, ((UINT16)V1e + V2e + V1o + V2o) / 4);
1416 *b2++ = Uavg;
1417 *b3++ = Vavg;
1418 }
1419
1420 /* UV from 2x, 2y+1 */
1421 if (b1Odd)
1422 {
1423 *b4++ = U1o;
1424 *b5++ = V1o;
1425
1426 if (!lastX)
1427 {
1428 *b4++ = U2o;
1429 *b5++ = V2o;
1430 }
1431 }
1432
1433 /* UV from 2x+1, 2y */
1434 if (!lastX)
1435 {
1436 *b6++ = U2e;
1437 *b7++ = V2e;
1438 }
1439 }
1440}
1441
1442void general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(size_t offset, const BYTE* WINPR_RESTRICT pSrcEven,
1443 const BYTE* WINPR_RESTRICT pSrcOdd,
1444 BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd,
1445 BYTE* WINPR_RESTRICT b2, BYTE* WINPR_RESTRICT b3,
1446 BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1447 BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7,
1448 UINT32 width)
1449{
1450 int_general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(offset, pSrcEven, pSrcOdd, b1Even, b1Odd, b2, b3, b4,
1451 b5, b6, b7, width);
1452}
1453
1454static inline pstatus_t general_RGBToAVC444YUV_BGRX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
1455 BYTE* WINPR_RESTRICT pDst1[3],
1456 const UINT32 dst1Step[3],
1457 BYTE* WINPR_RESTRICT pDst2[3],
1458 const UINT32 dst2Step[3],
1459 const prim_size_t* WINPR_RESTRICT roi)
1460{
1465 size_t y = 0;
1466 for (; y < roi->height - roi->height % 2; y += 2)
1467 {
1468 const BYTE* srcEven = pSrc + 1ULL * y * srcStep;
1469 const BYTE* srcOdd = pSrc + 1ULL * (y + 1) * srcStep;
1470 const size_t i = y >> 1;
1471 const size_t n = (i & (uint32_t)~7) + i;
1472 BYTE* b1Even = pDst1[0] + 1ULL * y * dst1Step[0];
1473 BYTE* b1Odd = (b1Even + dst1Step[0]);
1474 BYTE* b2 = pDst1[1] + 1ULL * (y / 2) * dst1Step[1];
1475 BYTE* b3 = pDst1[2] + 1ULL * (y / 2) * dst1Step[2];
1476 BYTE* b4 = pDst2[0] + 1ULL * dst2Step[0] * n;
1477 BYTE* b5 = b4 + 8ULL * dst2Step[0];
1478 BYTE* b6 = pDst2[1] + 1ULL * (y / 2) * dst2Step[1];
1479 BYTE* b7 = pDst2[2] + 1ULL * (y / 2) * dst2Step[2];
1480 int_general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(0, srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4,
1481 b5, b6, b7, roi->width);
1482 }
1483 for (; y < roi->height; y++)
1484 {
1485 const BYTE* srcEven = pSrc + 1ULL * y * srcStep;
1486 BYTE* b1Even = pDst1[0] + 1ULL * y * dst1Step[0];
1487 BYTE* b2 = pDst1[1] + 1ULL * (y / 2) * dst1Step[1];
1488 BYTE* b3 = pDst1[2] + 1ULL * (y / 2) * dst1Step[2];
1489 BYTE* b6 = pDst2[1] + 1ULL * (y / 2) * dst2Step[1];
1490 BYTE* b7 = pDst2[2] + 1ULL * (y / 2) * dst2Step[2];
1491 int_general_RGBToAVC444YUV_BGRX_DOUBLE_ROW(0, srcEven, nullptr, b1Even, nullptr, b2, b3,
1492 nullptr, nullptr, b6, b7, roi->width);
1493 }
1494
1495 return PRIMITIVES_SUCCESS;
1496}
1497
1498static inline void general_RGBToAVC444YUV_RGBX_DOUBLE_ROW(
1499 const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd,
1500 BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
1501 BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1502 BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
1503{
1504 for (UINT32 x = 0; x < width; x += 2)
1505 {
1506 const BOOL lastX = (x + 1) >= width;
1507 BYTE Y1e = 0;
1508 BYTE Y2e = 0;
1509 BYTE U1e = 0;
1510 BYTE V1e = 0;
1511 BYTE U2e = 0;
1512 BYTE V2e = 0;
1513 BYTE Y1o = 0;
1514 BYTE Y2o = 0;
1515 BYTE U1o = 0;
1516 BYTE V1o = 0;
1517 BYTE U2o = 0;
1518 BYTE V2o = 0;
1519 /* Read 4 pixels, 2 from even, 2 from odd lines */
1520 {
1521 const BYTE r = *srcEven++;
1522 const BYTE g = *srcEven++;
1523 const BYTE b = *srcEven++;
1524 srcEven++;
1525 Y1e = Y2e = Y1o = Y2o = RGB2Y(r, g, b);
1526 U1e = U2e = U1o = U2o = RGB2U(r, g, b);
1527 V1e = V2e = V1o = V2o = RGB2V(r, g, b);
1528 }
1529
1530 if (!lastX)
1531 {
1532 const BYTE r = *srcEven++;
1533 const BYTE g = *srcEven++;
1534 const BYTE b = *srcEven++;
1535 srcEven++;
1536 Y2e = RGB2Y(r, g, b);
1537 U2e = RGB2U(r, g, b);
1538 V2e = RGB2V(r, g, b);
1539 }
1540
1541 if (b1Odd)
1542 {
1543 const BYTE r = *srcOdd++;
1544 const BYTE g = *srcOdd++;
1545 const BYTE b = *srcOdd++;
1546 srcOdd++;
1547 Y1o = Y2o = RGB2Y(r, g, b);
1548 U1o = U2o = RGB2U(r, g, b);
1549 V1o = V2o = RGB2V(r, g, b);
1550 }
1551
1552 if (b1Odd && !lastX)
1553 {
1554 const BYTE r = *srcOdd++;
1555 const BYTE g = *srcOdd++;
1556 const BYTE b = *srcOdd++;
1557 srcOdd++;
1558 Y2o = RGB2Y(r, g, b);
1559 U2o = RGB2U(r, g, b);
1560 V2o = RGB2V(r, g, b);
1561 }
1562
1563 /* We have 4 Y pixels, so store them. */
1564 *b1Even++ = Y1e;
1565 *b1Even++ = Y2e;
1566
1567 if (b1Odd)
1568 {
1569 *b1Odd++ = Y1o;
1570 *b1Odd++ = Y2o;
1571 }
1572
1573 /* 2x 2y pixel in luma UV plane use averaging
1574 */
1575 {
1576 const BYTE Uavg = WINPR_ASSERTING_INT_CAST(BYTE, ((UINT16)U1e + U2e + U1o + U2o) / 4);
1577 const BYTE Vavg = WINPR_ASSERTING_INT_CAST(BYTE, ((UINT16)V1e + V2e + V1o + V2o) / 4);
1578 *b2++ = Uavg;
1579 *b3++ = Vavg;
1580 }
1581
1582 /* UV from 2x, 2y+1 */
1583 if (b1Odd)
1584 {
1585 *b4++ = U1o;
1586 *b5++ = V1o;
1587
1588 if (!lastX)
1589 {
1590 *b4++ = U2o;
1591 *b5++ = V2o;
1592 }
1593 }
1594
1595 /* UV from 2x+1, 2y */
1596 if (!lastX)
1597 {
1598 *b6++ = U2e;
1599 *b7++ = V2e;
1600 }
1601 }
1602}
1603
1604static inline pstatus_t general_RGBToAVC444YUV_RGBX(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcStep,
1605 BYTE* WINPR_RESTRICT pDst1[3],
1606 const UINT32 dst1Step[3],
1607 BYTE* WINPR_RESTRICT pDst2[3],
1608 const UINT32 dst2Step[3],
1609 const prim_size_t* WINPR_RESTRICT roi)
1610{
1616 size_t y = 0;
1617 for (; y < roi->height - roi->height % 2; y += 2)
1618 {
1619 const BOOL last = (y >= (roi->height - 1));
1620 const BYTE* srcEven = pSrc + 1ULL * y * srcStep;
1621 const BYTE* srcOdd = pSrc + 1ULL * (y + 1) * srcStep;
1622 const size_t i = y >> 1;
1623 const size_t n = (i & (size_t)~7) + i;
1624 BYTE* b1Even = pDst1[0] + 1ULL * y * dst1Step[0];
1625 BYTE* b1Odd = !last ? (b1Even + dst1Step[0]) : nullptr;
1626 BYTE* b2 = pDst1[1] + 1ULL * (y / 2) * dst1Step[1];
1627 BYTE* b3 = pDst1[2] + 1ULL * (y / 2) * dst1Step[2];
1628 BYTE* b4 = pDst2[0] + 1ULL * dst2Step[0] * n;
1629 BYTE* b5 = b4 + 8ULL * dst2Step[0];
1630 BYTE* b6 = pDst2[1] + 1ULL * (y / 2) * dst2Step[1];
1631 BYTE* b7 = pDst2[2] + 1ULL * (y / 2) * dst2Step[2];
1632 general_RGBToAVC444YUV_RGBX_DOUBLE_ROW(srcEven, srcOdd, b1Even, b1Odd, b2, b3, b4, b5, b6,
1633 b7, roi->width);
1634 }
1635 for (; y < roi->height; y++)
1636 {
1637 const BYTE* srcEven = pSrc + 1ULL * y * srcStep;
1638 BYTE* b1Even = pDst1[0] + 1ULL * y * dst1Step[0];
1639 BYTE* b2 = pDst1[1] + 1ULL * (y / 2) * dst1Step[1];
1640 BYTE* b3 = pDst1[2] + 1ULL * (y / 2) * dst1Step[2];
1641 BYTE* b6 = pDst2[1] + 1ULL * (y / 2) * dst2Step[1];
1642 BYTE* b7 = pDst2[2] + 1ULL * (y / 2) * dst2Step[2];
1643 general_RGBToAVC444YUV_RGBX_DOUBLE_ROW(srcEven, nullptr, b1Even, nullptr, b2, b3, nullptr,
1644 nullptr, b6, b7, roi->width);
1645 }
1646 return PRIMITIVES_SUCCESS;
1647}
1648
1649static inline void general_RGBToAVC444YUV_ANY_DOUBLE_ROW(
1650 const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd, UINT32 srcFormat,
1651 BYTE* WINPR_RESTRICT b1Even, BYTE* WINPR_RESTRICT b1Odd, BYTE* WINPR_RESTRICT b2,
1652 BYTE* WINPR_RESTRICT b3, BYTE* WINPR_RESTRICT b4, BYTE* WINPR_RESTRICT b5,
1653 BYTE* WINPR_RESTRICT b6, BYTE* WINPR_RESTRICT b7, UINT32 width)
1654{
1655 const UINT32 bpp = FreeRDPGetBytesPerPixel(srcFormat);
1656 for (UINT32 x = 0; x < width; x += 2)
1657 {
1658 const BOOL lastX = (x + 1) >= width;
1659 BYTE Y1e = 0;
1660 BYTE Y2e = 0;
1661 BYTE U1e = 0;
1662 BYTE V1e = 0;
1663 BYTE U2e = 0;
1664 BYTE V2e = 0;
1665 BYTE Y1o = 0;
1666 BYTE Y2o = 0;
1667 BYTE U1o = 0;
1668 BYTE V1o = 0;
1669 BYTE U2o = 0;
1670 BYTE V2o = 0;
1671 /* Read 4 pixels, 2 from even, 2 from odd lines */
1672 {
1673 BYTE r = 0;
1674 BYTE g = 0;
1675 BYTE b = 0;
1676 const UINT32 color = FreeRDPReadColor(srcEven, srcFormat);
1677 srcEven += bpp;
1678 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1679 Y1e = Y2e = Y1o = Y2o = RGB2Y(r, g, b);
1680 U1e = U2e = U1o = U2o = RGB2U(r, g, b);
1681 V1e = V2e = V1o = V2o = RGB2V(r, g, b);
1682 }
1683
1684 if (!lastX)
1685 {
1686 BYTE r = 0;
1687 BYTE g = 0;
1688 BYTE b = 0;
1689 const UINT32 color = FreeRDPReadColor(srcEven, srcFormat);
1690 srcEven += bpp;
1691 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1692 Y2e = RGB2Y(r, g, b);
1693 U2e = RGB2U(r, g, b);
1694 V2e = RGB2V(r, g, b);
1695 }
1696
1697 if (b1Odd)
1698 {
1699 BYTE r = 0;
1700 BYTE g = 0;
1701 BYTE b = 0;
1702 const UINT32 color = FreeRDPReadColor(srcOdd, srcFormat);
1703 srcOdd += bpp;
1704 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1705 Y1o = Y2o = RGB2Y(r, g, b);
1706 U1o = U2o = RGB2U(r, g, b);
1707 V1o = V2o = RGB2V(r, g, b);
1708 }
1709
1710 if (b1Odd && !lastX)
1711 {
1712 BYTE r = 0;
1713 BYTE g = 0;
1714 BYTE b = 0;
1715 const UINT32 color = FreeRDPReadColor(srcOdd, srcFormat);
1716 srcOdd += bpp;
1717 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1718 Y2o = RGB2Y(r, g, b);
1719 U2o = RGB2U(r, g, b);
1720 V2o = RGB2V(r, g, b);
1721 }
1722
1723 /* We have 4 Y pixels, so store them. */
1724 *b1Even++ = Y1e;
1725 *b1Even++ = Y2e;
1726
1727 if (b1Odd)
1728 {
1729 *b1Odd++ = Y1o;
1730 *b1Odd++ = Y2o;
1731 }
1732
1733 /* 2x 2y pixel in luma UV plane use averaging
1734 */
1735 {
1736 const BYTE Uavg = WINPR_ASSERTING_INT_CAST(
1737 BYTE, ((UINT16)U1e + (UINT16)U2e + (UINT16)U1o + (UINT16)U2o) / 4);
1738 const BYTE Vavg = WINPR_ASSERTING_INT_CAST(
1739 BYTE, ((UINT16)V1e + (UINT16)V2e + (UINT16)V1o + (UINT16)V2o) / 4);
1740 *b2++ = Uavg;
1741 *b3++ = Vavg;
1742 }
1743
1744 /* UV from 2x, 2y+1 */
1745 if (b1Odd)
1746 {
1747 *b4++ = U1o;
1748 *b5++ = V1o;
1749
1750 if (!lastX)
1751 {
1752 *b4++ = U2o;
1753 *b5++ = V2o;
1754 }
1755 }
1756
1757 /* UV from 2x+1, 2y */
1758 if (!lastX)
1759 {
1760 *b6++ = U2e;
1761 *b7++ = V2e;
1762 }
1763 }
1764}
1765
1766static inline pstatus_t
1767general_RGBToAVC444YUV_ANY(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat, UINT32 srcStep,
1768 BYTE* WINPR_RESTRICT pDst1[3], const UINT32 dst1Step[3],
1769 BYTE* WINPR_RESTRICT pDst2[3], const UINT32 dst2Step[3],
1770 const prim_size_t* WINPR_RESTRICT roi)
1771{
1829 const BYTE* pMaxSrc = pSrc + 1ULL * (roi->height - 1) * srcStep;
1830
1831 for (size_t y = 0; y < roi->height; y += 2)
1832 {
1833 WINPR_ASSERT(y < UINT32_MAX);
1834
1835 const BOOL last = (y >= (roi->height - 1));
1836 const BYTE* srcEven = y < roi->height ? pSrc + y * srcStep : pMaxSrc;
1837 const BYTE* srcOdd = !last ? pSrc + (y + 1) * srcStep : pMaxSrc;
1838 const UINT32 i = (UINT32)y >> 1;
1839 const UINT32 n = (i & (uint32_t)~7) + i;
1840 BYTE* b1Even = pDst1[0] + y * dst1Step[0];
1841 BYTE* b1Odd = !last ? (b1Even + dst1Step[0]) : nullptr;
1842 BYTE* b2 = pDst1[1] + (y / 2) * dst1Step[1];
1843 BYTE* b3 = pDst1[2] + (y / 2) * dst1Step[2];
1844 BYTE* b4 = pDst2[0] + 1ULL * dst2Step[0] * n;
1845 BYTE* b5 = b4 + 8ULL * dst2Step[0];
1846 BYTE* b6 = pDst2[1] + (y / 2) * dst2Step[1];
1847 BYTE* b7 = pDst2[2] + (y / 2) * dst2Step[2];
1848 general_RGBToAVC444YUV_ANY_DOUBLE_ROW(srcEven, srcOdd, srcFormat, b1Even, b1Odd, b2, b3, b4,
1849 b5, b6, b7, roi->width);
1850 }
1851
1852 return PRIMITIVES_SUCCESS;
1853}
1854
1855static inline pstatus_t general_RGBToAVC444YUV(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
1856 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[3],
1857 const UINT32 dst1Step[3],
1858 BYTE* WINPR_RESTRICT pDst2[3],
1859 const UINT32 dst2Step[3],
1860 const prim_size_t* WINPR_RESTRICT roi)
1861{
1862 if (!pSrc || !pDst1 || !dst1Step || !pDst2 || !dst2Step)
1863 return -1;
1864
1865 if (!pDst1[0] || !pDst1[1] || !pDst1[2])
1866 return -1;
1867
1868 if (!dst1Step[0] || !dst1Step[1] || !dst1Step[2])
1869 return -1;
1870
1871 if (!pDst2[0] || !pDst2[1] || !pDst2[2])
1872 return -1;
1873
1874 if (!dst2Step[0] || !dst2Step[1] || !dst2Step[2])
1875 return -1;
1876
1877 switch (srcFormat)
1878 {
1879
1880 case PIXEL_FORMAT_BGRA32:
1881 case PIXEL_FORMAT_BGRX32:
1882 return general_RGBToAVC444YUV_BGRX(pSrc, srcStep, pDst1, dst1Step, pDst2, dst2Step,
1883 roi);
1884
1885 case PIXEL_FORMAT_RGBA32:
1886 case PIXEL_FORMAT_RGBX32:
1887 return general_RGBToAVC444YUV_RGBX(pSrc, srcStep, pDst1, dst1Step, pDst2, dst2Step,
1888 roi);
1889
1890 default:
1891 return general_RGBToAVC444YUV_ANY(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
1892 dst2Step, roi);
1893 }
1894
1895 return !PRIMITIVES_SUCCESS;
1896}
1897
1898static inline void general_RGBToAVC444YUVv2_ANY_DOUBLE_ROW(
1899 const BYTE* WINPR_RESTRICT srcEven, const BYTE* WINPR_RESTRICT srcOdd, UINT32 srcFormat,
1900 BYTE* WINPR_RESTRICT yLumaDstEven, BYTE* WINPR_RESTRICT yLumaDstOdd,
1901 BYTE* WINPR_RESTRICT uLumaDst, BYTE* WINPR_RESTRICT vLumaDst,
1902 BYTE* WINPR_RESTRICT yEvenChromaDst1, BYTE* WINPR_RESTRICT yEvenChromaDst2,
1903 BYTE* WINPR_RESTRICT yOddChromaDst1, BYTE* WINPR_RESTRICT yOddChromaDst2,
1904 BYTE* WINPR_RESTRICT uChromaDst1, BYTE* WINPR_RESTRICT uChromaDst2,
1905 BYTE* WINPR_RESTRICT vChromaDst1, BYTE* WINPR_RESTRICT vChromaDst2, UINT32 width)
1906{
1907 const UINT32 bpp = FreeRDPGetBytesPerPixel(srcFormat);
1908
1909 for (UINT32 x = 0; x < width; x += 2)
1910 {
1911 BYTE Ya = 0;
1912 BYTE Ua = 0;
1913 BYTE Va = 0;
1914 BYTE Yb = 0;
1915 BYTE Ub = 0;
1916 BYTE Vb = 0;
1917 BYTE Yc = 0;
1918 BYTE Uc = 0;
1919 BYTE Vc = 0;
1920 BYTE Yd = 0;
1921 BYTE Ud = 0;
1922 BYTE Vd = 0;
1923 {
1924 BYTE b = 0;
1925 BYTE g = 0;
1926 BYTE r = 0;
1927 const UINT32 color = FreeRDPReadColor(srcEven, srcFormat);
1928 srcEven += bpp;
1929 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1930 Ya = RGB2Y(r, g, b);
1931 Ua = RGB2U(r, g, b);
1932 Va = RGB2V(r, g, b);
1933 }
1934
1935 if (x < width - 1)
1936 {
1937 BYTE b = 0;
1938 BYTE g = 0;
1939 BYTE r = 0;
1940 const UINT32 color = FreeRDPReadColor(srcEven, srcFormat);
1941 srcEven += bpp;
1942 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1943 Yb = RGB2Y(r, g, b);
1944 Ub = RGB2U(r, g, b);
1945 Vb = RGB2V(r, g, b);
1946 }
1947 else
1948 {
1949 Yb = Ya;
1950 Ub = Ua;
1951 Vb = Va;
1952 }
1953
1954 if (srcOdd)
1955 {
1956 BYTE b = 0;
1957 BYTE g = 0;
1958 BYTE r = 0;
1959 const UINT32 color = FreeRDPReadColor(srcOdd, srcFormat);
1960 srcOdd += bpp;
1961 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1962 Yc = RGB2Y(r, g, b);
1963 Uc = RGB2U(r, g, b);
1964 Vc = RGB2V(r, g, b);
1965 }
1966 else
1967 {
1968 Yc = Ya;
1969 Uc = Ua;
1970 Vc = Va;
1971 }
1972
1973 if (srcOdd && (x < width - 1))
1974 {
1975 BYTE b = 0;
1976 BYTE g = 0;
1977 BYTE r = 0;
1978 const UINT32 color = FreeRDPReadColor(srcOdd, srcFormat);
1979 srcOdd += bpp;
1980 FreeRDPSplitColor(color, srcFormat, &r, &g, &b, nullptr, nullptr);
1981 Yd = RGB2Y(r, g, b);
1982 Ud = RGB2U(r, g, b);
1983 Vd = RGB2V(r, g, b);
1984 }
1985 else
1986 {
1987 Yd = Ya;
1988 Ud = Ua;
1989 Vd = Va;
1990 }
1991
1992 /* Y [b1] */
1993 *yLumaDstEven++ = Ya;
1994
1995 if (x < width - 1)
1996 *yLumaDstEven++ = Yb;
1997
1998 if (srcOdd)
1999 *yLumaDstOdd++ = Yc;
2000
2001 if (srcOdd && (x < width - 1))
2002 *yLumaDstOdd++ = Yd;
2003
2004 /* 2x 2y [b2,b3] */
2005 *uLumaDst++ = (Ua + Ub + Uc + Ud) / 4;
2006 *vLumaDst++ = (Va + Vb + Vc + Vd) / 4;
2007
2008 /* 2x+1, y [b4,b5] even */
2009 if (x < width - 1)
2010 {
2011 *yEvenChromaDst1++ = Ub;
2012 *yEvenChromaDst2++ = Vb;
2013 }
2014
2015 if (srcOdd)
2016 {
2017 /* 2x+1, y [b4,b5] odd */
2018 if (x < width - 1)
2019 {
2020 *yOddChromaDst1++ = Ud;
2021 *yOddChromaDst2++ = Vd;
2022 }
2023
2024 /* 4x 2y+1 [b6, b7] */
2025 if (x % 4 == 0)
2026 {
2027 *uChromaDst1++ = Uc;
2028 *uChromaDst2++ = Vc;
2029 }
2030 /* 4x+2 2y+1 [b8, b9] */
2031 else
2032 {
2033 *vChromaDst1++ = Uc;
2034 *vChromaDst2++ = Vc;
2035 }
2036 }
2037 }
2038}
2039
2040static inline pstatus_t
2041general_RGBToAVC444YUVv2_ANY(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat, UINT32 srcStep,
2042 BYTE* WINPR_RESTRICT pDst1[3], const UINT32 dst1Step[3],
2043 BYTE* WINPR_RESTRICT pDst2[3], const UINT32 dst2Step[3],
2044 const prim_size_t* WINPR_RESTRICT roi)
2045{
2096 if (roi->height < 1 || roi->width < 1)
2097 return !PRIMITIVES_SUCCESS;
2098
2099 size_t y = 0;
2100 for (; y < roi->height - roi->height % 2; y += 2)
2101 {
2102 const BYTE* srcEven = (pSrc + y * srcStep);
2103 const BYTE* srcOdd = (y < roi->height - 1) ? (srcEven + srcStep) : nullptr;
2104 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
2105 BYTE* dstLumaYOdd = (dstLumaYEven + dst1Step[0]);
2106 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
2107 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
2108 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
2109 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
2110 BYTE* dstOddChromaY1 = dstEvenChromaY1 + dst2Step[0];
2111 BYTE* dstOddChromaY2 = dstEvenChromaY2 + dst2Step[0];
2112 BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
2113 BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
2114 BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
2115 BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
2116 general_RGBToAVC444YUVv2_ANY_DOUBLE_ROW(
2117 srcEven, srcOdd, srcFormat, dstLumaYEven, dstLumaYOdd, dstLumaU, dstLumaV,
2118 dstEvenChromaY1, dstEvenChromaY2, dstOddChromaY1, dstOddChromaY2, dstChromaU1,
2119 dstChromaU2, dstChromaV1, dstChromaV2, roi->width);
2120 }
2121 for (; y < roi->height; y++)
2122 {
2123 const BYTE* srcEven = (pSrc + y * srcStep);
2124 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
2125 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
2126 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
2127 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
2128 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
2129 general_RGBToAVC444YUVv2_ANY_DOUBLE_ROW(
2130 srcEven, nullptr, srcFormat, dstLumaYEven, nullptr, dstLumaU, dstLumaV, dstEvenChromaY1,
2131 dstEvenChromaY2, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, roi->width);
2132 }
2133
2134 return PRIMITIVES_SUCCESS;
2135}
2136
2137static inline void int_general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
2138 size_t offset, const BYTE* WINPR_RESTRICT pSrcEven, const BYTE* WINPR_RESTRICT pSrcOdd,
2139 BYTE* WINPR_RESTRICT yLumaDstEven, BYTE* WINPR_RESTRICT yLumaDstOdd,
2140 BYTE* WINPR_RESTRICT uLumaDst, BYTE* WINPR_RESTRICT vLumaDst,
2141 BYTE* WINPR_RESTRICT yEvenChromaDst1, BYTE* WINPR_RESTRICT yEvenChromaDst2,
2142 BYTE* WINPR_RESTRICT yOddChromaDst1, BYTE* WINPR_RESTRICT yOddChromaDst2,
2143 BYTE* WINPR_RESTRICT uChromaDst1, BYTE* WINPR_RESTRICT uChromaDst2,
2144 BYTE* WINPR_RESTRICT vChromaDst1, BYTE* WINPR_RESTRICT vChromaDst2, UINT32 width)
2145{
2146 WINPR_ASSERT(pSrcEven);
2147 WINPR_ASSERT(yLumaDstEven);
2148 WINPR_ASSERT(uLumaDst);
2149 WINPR_ASSERT(vLumaDst);
2150
2151 for (size_t x = offset; x < width; x += 2)
2152 {
2153 const BYTE* srcEven = &pSrcEven[4ULL * x];
2154 const BYTE* srcOdd = pSrcOdd ? &pSrcOdd[4ULL * x] : nullptr;
2155 BYTE Ya = 0;
2156 BYTE Ua = 0;
2157 BYTE Va = 0;
2158 BYTE Yb = 0;
2159 BYTE Ub = 0;
2160 BYTE Vb = 0;
2161 BYTE Yc = 0;
2162 BYTE Uc = 0;
2163 BYTE Vc = 0;
2164 BYTE Yd = 0;
2165 BYTE Ud = 0;
2166 BYTE Vd = 0;
2167 {
2168 const BYTE b = *srcEven++;
2169 const BYTE g = *srcEven++;
2170 const BYTE r = *srcEven++;
2171 srcEven++;
2172 Ya = RGB2Y(r, g, b);
2173 Ua = RGB2U(r, g, b);
2174 Va = RGB2V(r, g, b);
2175 }
2176
2177 if (x < width - 1)
2178 {
2179 const BYTE b = *srcEven++;
2180 const BYTE g = *srcEven++;
2181 const BYTE r = *srcEven++;
2182 srcEven++;
2183 Yb = RGB2Y(r, g, b);
2184 Ub = RGB2U(r, g, b);
2185 Vb = RGB2V(r, g, b);
2186 }
2187 else
2188 {
2189 Yb = Ya;
2190 Ub = Ua;
2191 Vb = Va;
2192 }
2193
2194 if (srcOdd)
2195 {
2196 const BYTE b = *srcOdd++;
2197 const BYTE g = *srcOdd++;
2198 const BYTE r = *srcOdd++;
2199 srcOdd++;
2200 Yc = RGB2Y(r, g, b);
2201 Uc = RGB2U(r, g, b);
2202 Vc = RGB2V(r, g, b);
2203 }
2204 else
2205 {
2206 Yc = Ya;
2207 Uc = Ua;
2208 Vc = Va;
2209 }
2210
2211 if (srcOdd && (x < width - 1))
2212 {
2213 const BYTE b = *srcOdd++;
2214 const BYTE g = *srcOdd++;
2215 const BYTE r = *srcOdd++;
2216 srcOdd++;
2217 Yd = RGB2Y(r, g, b);
2218 Ud = RGB2U(r, g, b);
2219 Vd = RGB2V(r, g, b);
2220 }
2221 else
2222 {
2223 Yd = Ya;
2224 Ud = Ua;
2225 Vd = Va;
2226 }
2227
2228 /* Y [b1] */
2229 *yLumaDstEven++ = Ya;
2230
2231 if (x < width - 1)
2232 *yLumaDstEven++ = Yb;
2233
2234 if (srcOdd && yLumaDstOdd)
2235 *yLumaDstOdd++ = Yc;
2236
2237 if (srcOdd && (x < width - 1) && yLumaDstOdd)
2238 *yLumaDstOdd++ = Yd;
2239
2240 /* 2x 2y [b2,b3] */
2241 *uLumaDst++ = (Ua + Ub + Uc + Ud) / 4;
2242 *vLumaDst++ = (Va + Vb + Vc + Vd) / 4;
2243
2244 /* 2x+1, y [b4,b5] even */
2245 if (x < width - 1)
2246 {
2247 *yEvenChromaDst1++ = Ub;
2248 *yEvenChromaDst2++ = Vb;
2249 }
2250
2251 if (srcOdd)
2252 {
2253 /* 2x+1, y [b4,b5] odd */
2254 if (x < width - 1)
2255 {
2256 *yOddChromaDst1++ = Ud;
2257 *yOddChromaDst2++ = Vd;
2258 }
2259
2260 /* 4x 2y+1 [b6, b7] */
2261 if (x % 4 == 0)
2262 {
2263 *uChromaDst1++ = Uc;
2264 *uChromaDst2++ = Vc;
2265 }
2266 /* 4x+2 2y+1 [b8, b9] */
2267 else
2268 {
2269 *vChromaDst1++ = Uc;
2270 *vChromaDst2++ = Vc;
2271 }
2272 }
2273 }
2274}
2275
2276void general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
2277 size_t offset, const BYTE* WINPR_RESTRICT pSrcEven, const BYTE* WINPR_RESTRICT pSrcOdd,
2278 BYTE* WINPR_RESTRICT yLumaDstEven, BYTE* WINPR_RESTRICT yLumaDstOdd,
2279 BYTE* WINPR_RESTRICT uLumaDst, BYTE* WINPR_RESTRICT vLumaDst,
2280 BYTE* WINPR_RESTRICT yEvenChromaDst1, BYTE* WINPR_RESTRICT yEvenChromaDst2,
2281 BYTE* WINPR_RESTRICT yOddChromaDst1, BYTE* WINPR_RESTRICT yOddChromaDst2,
2282 BYTE* WINPR_RESTRICT uChromaDst1, BYTE* WINPR_RESTRICT uChromaDst2,
2283 BYTE* WINPR_RESTRICT vChromaDst1, BYTE* WINPR_RESTRICT vChromaDst2, UINT32 width)
2284{
2285 int_general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
2286 offset, pSrcEven, pSrcOdd, yLumaDstEven, yLumaDstOdd, uLumaDst, vLumaDst, yEvenChromaDst1,
2287 yEvenChromaDst2, yOddChromaDst1, yOddChromaDst2, uChromaDst1, uChromaDst2, vChromaDst1,
2288 vChromaDst2, width);
2289}
2290
2291static inline pstatus_t general_RGBToAVC444YUVv2_BGRX(const BYTE* WINPR_RESTRICT pSrc,
2292 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[3],
2293 const UINT32 dst1Step[3],
2294 BYTE* WINPR_RESTRICT pDst2[3],
2295 const UINT32 dst2Step[3],
2296 const prim_size_t* WINPR_RESTRICT roi)
2297{
2298 if (roi->height < 1 || roi->width < 1)
2299 return !PRIMITIVES_SUCCESS;
2300
2301 size_t y = 0;
2302 for (; y < roi->height - roi->height % 2; y += 2)
2303 {
2304 const BYTE* srcEven = (pSrc + y * srcStep);
2305 const BYTE* srcOdd = (srcEven + srcStep);
2306 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
2307 BYTE* dstLumaYOdd = (dstLumaYEven + dst1Step[0]);
2308 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
2309 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
2310 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
2311 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
2312 BYTE* dstOddChromaY1 = dstEvenChromaY1 + dst2Step[0];
2313 BYTE* dstOddChromaY2 = dstEvenChromaY2 + dst2Step[0];
2314 BYTE* dstChromaU1 = (pDst2[1] + (y / 2) * dst2Step[1]);
2315 BYTE* dstChromaV1 = (pDst2[2] + (y / 2) * dst2Step[2]);
2316 BYTE* dstChromaU2 = dstChromaU1 + roi->width / 4;
2317 BYTE* dstChromaV2 = dstChromaV1 + roi->width / 4;
2318 int_general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
2319 0, srcEven, srcOdd, dstLumaYEven, dstLumaYOdd, dstLumaU, dstLumaV, dstEvenChromaY1,
2320 dstEvenChromaY2, dstOddChromaY1, dstOddChromaY2, dstChromaU1, dstChromaU2, dstChromaV1,
2321 dstChromaV2, roi->width);
2322 }
2323 for (; y < roi->height; y++)
2324 {
2325 const BYTE* srcEven = (pSrc + y * srcStep);
2326 BYTE* dstLumaYEven = (pDst1[0] + y * dst1Step[0]);
2327 BYTE* dstLumaU = (pDst1[1] + (y / 2) * dst1Step[1]);
2328 BYTE* dstLumaV = (pDst1[2] + (y / 2) * dst1Step[2]);
2329 BYTE* dstEvenChromaY1 = (pDst2[0] + y * dst2Step[0]);
2330 BYTE* dstEvenChromaY2 = dstEvenChromaY1 + roi->width / 2;
2331 int_general_RGBToAVC444YUVv2_BGRX_DOUBLE_ROW(
2332 0, srcEven, nullptr, dstLumaYEven, nullptr, dstLumaU, dstLumaV, dstEvenChromaY1,
2333 dstEvenChromaY2, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, roi->width);
2334 }
2335
2336 return PRIMITIVES_SUCCESS;
2337}
2338
2339static pstatus_t general_RGBToAVC444YUVv2(const BYTE* WINPR_RESTRICT pSrc, UINT32 srcFormat,
2340 UINT32 srcStep, BYTE* WINPR_RESTRICT pDst1[3],
2341 const UINT32 dst1Step[3], BYTE* WINPR_RESTRICT pDst2[3],
2342 const UINT32 dst2Step[3],
2343 const prim_size_t* WINPR_RESTRICT roi)
2344{
2345 switch (srcFormat)
2346 {
2347 case PIXEL_FORMAT_BGRA32:
2348 case PIXEL_FORMAT_BGRX32:
2349 return general_RGBToAVC444YUVv2_BGRX(pSrc, srcStep, pDst1, dst1Step, pDst2, dst2Step,
2350 roi);
2351
2352 default:
2353 return general_RGBToAVC444YUVv2_ANY(pSrc, srcFormat, srcStep, pDst1, dst1Step, pDst2,
2354 dst2Step, roi);
2355 }
2356
2357 return !PRIMITIVES_SUCCESS;
2358}
2359
2360void primitives_init_YUV(primitives_t* WINPR_RESTRICT prims)
2361{
2362 prims->YUV420ToRGB_8u_P3AC4R = general_YUV420ToRGB_8u_P3AC4R;
2363 prims->YUV444ToRGB_8u_P3AC4R = general_YUV444ToRGB_8u_P3AC4R;
2364 prims->RGBToYUV420_8u_P3AC4R = general_RGBToYUV420_8u_P3AC4R;
2365 prims->RGBToYUV444_8u_P3AC4R = general_RGBToYUV444_8u_P3AC4R;
2366 prims->RGBToI444_8u = general_RGBToI444_8u;
2367 prims->YUV420CombineToYUV444 = general_YUV420CombineToYUV444;
2368 prims->YUV444SplitToYUV420 = general_YUV444SplitToYUV420;
2369 prims->RGBToAVC444YUV = general_RGBToAVC444YUV;
2370 prims->RGBToAVC444YUVv2 = general_RGBToAVC444YUVv2;
2371}
2372
2373void primitives_init_YUV_opt(primitives_t* WINPR_RESTRICT prims)
2374{
2375 primitives_init_YUV(prims);
2376 primitives_init_YUV_sse41(prims);
2377 primitives_init_YUV_neon(prims);
2378}