FreeRDP
Loading...
Searching...
No Matches
TestPrimitivesYUV.c
1
2#include <freerdp/config.h>
3
4#include <stdlib.h>
5#include <math.h>
6#include <errno.h>
7
8#include "prim_test.h"
9
10#include <winpr/print.h>
11
12#include <winpr/wlog.h>
13#include <winpr/crypto.h>
14#include <freerdp/primitives.h>
15#include <freerdp/utils/profiler.h>
16
17#include "../prim_internal.h"
18
19#define TAG __FILE__
20
21#define PADDING_FILL_VALUE 0x37
22
23/* YUV to RGB conversion is lossy, so consider every value only
24 * differing by less than 2 abs equal. */
25WINPR_ATTR_NODISCARD
26static BOOL similar(const BYTE* src, const BYTE* dst, size_t size)
27{
28 for (size_t x = 0; x < size; x++)
29 {
30 int diff = src[x] - dst[x];
31
32 if (abs(diff) > 4)
33 {
34 (void)fprintf(stderr, "%" PRIuz " %02" PRIX8 " : %02" PRIX8 " diff=%d\n", x, src[x],
35 dst[x], abs(diff));
36 return FALSE;
37 }
38 }
39
40 return TRUE;
41}
42
43WINPR_ATTR_NODISCARD
44static BOOL similarRGB(size_t y, const BYTE* src, const BYTE* dst, size_t size, UINT32 format,
45 BOOL use444)
46{
47 BOOL rc = TRUE;
48 /* Skip YUV420, too lossy */
49 if (!use444)
50 return TRUE;
51
52 const UINT32 bpp = FreeRDPGetBytesPerPixel(format);
53 BYTE fill = PADDING_FILL_VALUE;
54 if (!FreeRDPColorHasAlpha(format))
55 fill = 0xFF;
56
57 for (size_t x = 0; x < size; x++)
58 {
59 const LONG maxDiff = 4;
60 UINT32 sColor = 0;
61 UINT32 dColor = 0;
62 BYTE sR = 0;
63 BYTE sG = 0;
64 BYTE sB = 0;
65 BYTE sA = 0;
66 BYTE dR = 0;
67 BYTE dG = 0;
68 BYTE dB = 0;
69 BYTE dA = 0;
70 sColor = FreeRDPReadColor(src, format);
71 dColor = FreeRDPReadColor(dst, format);
72 src += bpp;
73 dst += bpp;
74 FreeRDPSplitColor(sColor, format, &sR, &sG, &sB, &sA, nullptr);
75 FreeRDPSplitColor(dColor, format, &dR, &dG, &dB, &dA, nullptr);
76
77 const long diffr = labs(1L * sR - dR);
78 const long diffg = labs(1L * sG - dG);
79 const long diffb = labs(1L * sB - dB);
80 if ((diffr > maxDiff) || (diffg > maxDiff) || (diffb > maxDiff))
81 {
82 /* AVC444 uses an averaging filter for luma pixel U/V and reverses it in YUV444 -> RGB
83 * this is lossy and does not handle all combinations well so the 2x,2y pixel can be
84 * quite different after RGB -> YUV444 -> RGB conversion.
85 *
86 * skip these pixels to avoid failing the test
87 */
88 if (use444 && ((x % 2) == 0) && ((y % 2) == 0))
89 {
90 continue;
91 }
92
93 const BYTE sY = RGB2Y(sR, sG, sB);
94 const BYTE sU = RGB2U(sR, sG, sB);
95 const BYTE sV = RGB2V(sR, sG, sB);
96 const BYTE dY = RGB2Y(dR, dG, dB);
97 const BYTE dU = RGB2U(dR, dG, dB);
98 const BYTE dV = RGB2V(dR, dG, dB);
99 (void)fprintf(stderr,
100 "[%s] Color value mismatch R[%02X %02X], G[%02X %02X], B[%02X %02X] at "
101 "position %" PRIuz "x%" PRIuz "\n",
102 use444 ? "AVC444" : "AVC420", sR, dR, sG, dG, sA, dA, x, y);
103 (void)fprintf(stderr,
104 "[%s] Color value mismatch Y[%02X %02X], U[%02X %02X], V[%02X %02X] at "
105 "position %" PRIuz "x%" PRIuz "\n",
106 use444 ? "AVC444" : "AVC420", sY, dY, sU, dU, sV, dV, x, y);
107 rc = FALSE;
108 continue;
109 }
110
111 if (dA != fill)
112 {
113 (void)fprintf(
114 stderr,
115 "[%s] Invalid destination alpha value 0x%02X [expected 0x%02X] at position %" PRIuz
116 "x%" PRIuz "\n",
117 use444 ? "AVC444" : "AVC420", dA, fill, x, y);
118 rc = FALSE;
119 continue;
120 }
121 }
122
123 return rc;
124}
125
126WINPR_ATTR_NODISCARD
127static BOOL get_size(BOOL large, UINT32* width, UINT32* height)
128{
129 UINT32 shift = large ? 8 : 1;
130 if (winpr_RAND(width, sizeof(*width)) < 0)
131 return FALSE;
132 if (winpr_RAND(height, sizeof(*height)) < 0)
133 return FALSE;
134 *width = (*width % 64 + 1) << shift;
135 *height = (*height % 64 + 1);
136 return TRUE;
137}
138
139WINPR_ATTR_NODISCARD
140static BOOL check_padding(const BYTE* psrc, size_t size, size_t padding, const char* buffer)
141{
142 BOOL rc = TRUE;
143 const BYTE* src = nullptr;
144 const BYTE* esrc = nullptr;
145 size_t halfPad = (padding + 1) / 2;
146
147 if (!psrc)
148 return FALSE;
149
150 src = psrc - halfPad;
151 esrc = src + size + halfPad;
152
153 for (size_t x = 0; x < halfPad; x++)
154 {
155 const BYTE s = *src++;
156 const BYTE d = *esrc++;
157
158 if (s != 'A')
159 {
160 size_t start = x;
161
162 while ((x < halfPad) && (*esrc++ != 'A'))
163 x++;
164
165 (void)fprintf(stderr,
166 "Buffer underflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
167 "]\n",
168 d, 'A', buffer, start, x);
169 return FALSE;
170 }
171
172 if (d != 'A')
173 {
174 size_t start = x;
175
176 while ((x < halfPad) && (*esrc++ != 'A'))
177 x++;
178
179 (void)fprintf(stderr,
180 "Buffer overflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
181 "]\n",
182 d, 'A', buffer, start, x);
183 return FALSE;
184 }
185 }
186
187 return rc;
188}
189
190static void* set_padding(size_t size, size_t padding)
191{
192 size_t halfPad = (padding + 1) / 2;
193 BYTE* psrc = nullptr;
194 BYTE* src = winpr_aligned_calloc(1, size + 2 * halfPad, 16);
195
196 if (!src)
197 return nullptr;
198
199 memset(&src[0], 'A', halfPad);
200 memset(&src[halfPad], PADDING_FILL_VALUE, size);
201 memset(&src[halfPad + size], 'A', halfPad);
202 psrc = &src[halfPad];
203
204 if (!check_padding(psrc, size, padding, "init"))
205 {
206 winpr_aligned_free(src);
207 return nullptr;
208 }
209
210 return psrc;
211}
212
213static void free_padding(void* src, size_t padding)
214{
215 BYTE* ptr = nullptr;
216
217 if (!src)
218 return;
219
220 ptr = ((BYTE*)src) - (padding + 1) / 2;
221 winpr_aligned_free(ptr);
222}
223
224/* Create 2 pseudo YUV420 frames of same size.
225 * Combine them and check, if the data is at the expected position. */
226static BOOL TestPrimitiveYUVCombine(primitives_t* prims, prim_size_t roi)
227{
228 union
229 {
230 const BYTE** cpv;
231 BYTE** pv;
232 } cnv;
233 size_t awidth = 0;
234 size_t aheight = 0;
235 BOOL rc = FALSE;
236 BYTE* luma[3] = WINPR_C_ARRAY_INIT;
237 BYTE* chroma[3] = WINPR_C_ARRAY_INIT;
238 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
239 BYTE* pmain[3] = WINPR_C_ARRAY_INIT;
240 BYTE* paux[3] = WINPR_C_ARRAY_INIT;
241 UINT32 lumaStride[3] = WINPR_C_ARRAY_INIT;
242 UINT32 chromaStride[3] = WINPR_C_ARRAY_INIT;
243 UINT32 yuvStride[3] = WINPR_C_ARRAY_INIT;
244 const size_t padding = 10000;
245 RECTANGLE_16 rect = WINPR_C_ARRAY_INIT;
246 PROFILER_DEFINE(yuvCombine)
247 PROFILER_DEFINE(yuvSplit)
248
249 // TODO: we only support even height values at the moment
250 if (roi.height % 2)
251 roi.height++;
252
253 awidth = roi.width + 16 - roi.width % 16;
254 aheight = roi.height + 16 - roi.height % 16;
255 (void)fprintf(stderr,
256 "Running YUVCombine on frame size %" PRIu32 "x%" PRIu32 " [%" PRIuz "x%" PRIuz
257 "]\n",
258 roi.width, roi.height, awidth, aheight);
259 PROFILER_CREATE(yuvCombine, "YUV420CombineToYUV444")
260 PROFILER_CREATE(yuvSplit, "YUV444SplitToYUV420")
261 rect.left = 0;
262 rect.top = 0;
263 rect.right = roi.width;
264 rect.bottom = roi.height;
265
266 if (!prims || !prims->YUV420CombineToYUV444)
267 goto fail;
268
269 for (UINT32 x = 0; x < 3; x++)
270 {
271 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
272 size_t size = aheight * awidth;
273 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
274 yuvStride[x] = awidth;
275
276 if (!(yuv[x] = set_padding(size, padding)))
277 goto fail;
278
279 lumaStride[x] = halfStride;
280
281 if (!(luma[x] = set_padding(halfSize, padding)))
282 goto fail;
283
284 if (!(pmain[x] = set_padding(halfSize, padding)))
285 goto fail;
286
287 chromaStride[x] = halfStride;
288
289 if (!(chroma[x] = set_padding(halfSize, padding)))
290 goto fail;
291
292 if (!(paux[x] = set_padding(halfSize, padding)))
293 goto fail;
294
295 memset(luma[x], WINPR_ASSERTING_INT_CAST(int, 0xAB + 3 * x), halfSize);
296 memset(chroma[x], WINPR_ASSERTING_INT_CAST(int, 0x80 + 2 * x), halfSize);
297
298 if (!check_padding(luma[x], halfSize, padding, "luma"))
299 goto fail;
300
301 if (!check_padding(chroma[x], halfSize, padding, "chroma"))
302 goto fail;
303
304 if (!check_padding(pmain[x], halfSize, padding, "main"))
305 goto fail;
306
307 if (!check_padding(paux[x], halfSize, padding, "aux"))
308 goto fail;
309
310 if (!check_padding(yuv[x], size, padding, "yuv"))
311 goto fail;
312 }
313
314 PROFILER_ENTER(yuvCombine)
315
316 cnv.pv = luma;
317 if (prims->YUV420CombineToYUV444(AVC444_LUMA, cnv.cpv, lumaStride, roi.width, roi.height, yuv,
318 yuvStride, &rect) != PRIMITIVES_SUCCESS)
319 {
320 PROFILER_EXIT(yuvCombine)
321 goto fail;
322 }
323
324 cnv.pv = chroma;
325 if (prims->YUV420CombineToYUV444(AVC444_CHROMAv1, cnv.cpv, chromaStride, roi.width, roi.height,
326 yuv, yuvStride, &rect) != PRIMITIVES_SUCCESS)
327 {
328 PROFILER_EXIT(yuvCombine)
329 goto fail;
330 }
331
332 PROFILER_EXIT(yuvCombine)
333
334 for (size_t x = 0; x < 3; x++)
335 {
336 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
337 size_t size = 1ULL * aheight * awidth;
338 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
339
340 if (!check_padding(luma[x], halfSize, padding, "luma"))
341 goto fail;
342
343 if (!check_padding(chroma[x], halfSize, padding, "chroma"))
344 goto fail;
345
346 if (!check_padding(yuv[x], size, padding, "yuv"))
347 goto fail;
348 }
349
350 PROFILER_ENTER(yuvSplit)
351
352 cnv.pv = yuv;
353 if (prims->YUV444SplitToYUV420(cnv.cpv, yuvStride, pmain, lumaStride, paux, chromaStride,
354 &roi) != PRIMITIVES_SUCCESS)
355 {
356 PROFILER_EXIT(yuvSplit)
357 goto fail;
358 }
359
360 PROFILER_EXIT(yuvSplit)
361
362 for (UINT32 x = 0; x < 3; x++)
363 {
364 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
365 size_t size = aheight * awidth;
366 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
367
368 if (!check_padding(pmain[x], halfSize, padding, "main"))
369 goto fail;
370
371 if (!check_padding(paux[x], halfSize, padding, "aux"))
372 goto fail;
373
374 if (!check_padding(yuv[x], size, padding, "yuv"))
375 goto fail;
376 }
377
378 for (size_t i = 0; i < 3; i++)
379 {
380 for (size_t y = 0; y < roi.height; y++)
381 {
382 UINT32 w = roi.width;
383 UINT32 lstride = lumaStride[i];
384 UINT32 cstride = chromaStride[i];
385
386 if (i > 0)
387 {
388 w = (roi.width + 3) / 4;
389
390 if (roi.height > (roi.height + 1) / 2)
391 continue;
392 }
393
394 if (!similar(luma[i] + y * lstride, pmain[i] + y * lstride, w))
395 goto fail;
396
397 /* Need to ignore lines of destination Y plane,
398 * if the lines are not a multiple of 16
399 * as the UV planes are packed in 8 line stripes. */
400 if (i == 0)
401 {
402 /* TODO: This check is not perfect, it does not
403 * include the last V lines packed to the Y
404 * frame. */
405 UINT32 rem = roi.height % 16;
406
407 if (y > roi.height - rem)
408 continue;
409 }
410
411 if (!similar(chroma[i] + y * cstride, paux[i] + y * cstride, w))
412 goto fail;
413 }
414 }
415
416 PROFILER_PRINT_HEADER
417 PROFILER_PRINT(yuvSplit)
418 PROFILER_PRINT(yuvCombine)
419 PROFILER_PRINT_FOOTER
420 rc = TRUE;
421fail:
422 printf("[%s] run %s.\n", __func__, (rc) ? "SUCCESS" : "FAILED");
423 PROFILER_FREE(yuvCombine)
424 PROFILER_FREE(yuvSplit)
425
426 for (UINT32 x = 0; x < 3; x++)
427 {
428 free_padding(yuv[x], padding);
429 free_padding(luma[x], padding);
430 free_padding(chroma[x], padding);
431 free_padding(pmain[x], padding);
432 free_padding(paux[x], padding);
433 }
434
435 return rc;
436}
437
438static BOOL TestPrimitiveYUV(primitives_t* prims, prim_size_t roi, BOOL use444)
439{
440 union
441 {
442 const BYTE** cpv;
443 BYTE** pv;
444 } cnv;
445 BOOL res = FALSE;
446 UINT32 awidth = 0;
447 UINT32 aheight = 0;
448 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
449 UINT32 yuv_step[3] = WINPR_C_ARRAY_INIT;
450 BYTE* rgb = nullptr;
451 BYTE* rgb_dst = nullptr;
452 size_t size = 0;
453 size_t uvsize = 0;
454 size_t uvwidth = 0;
455 size_t padding = 100ULL * 16ULL;
456 UINT32 stride = 0;
457 const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
458 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
459 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
460 PROFILER_DEFINE(rgbToYUV420)
461 PROFILER_DEFINE(rgbToYUV444)
462 PROFILER_DEFINE(yuv420ToRGB)
463 PROFILER_DEFINE(yuv444ToRGB)
464 /* Buffers need to be 16x16 aligned. */
465 awidth = roi.width + 16 - roi.width % 16;
466 aheight = roi.height + 16 - roi.height % 16;
467 stride = 1ULL * awidth * sizeof(UINT32);
468 size = 1ULL * awidth * aheight;
469
470 if (use444)
471 {
472 uvwidth = awidth;
473 uvsize = size;
474
475 if (!prims || !prims->RGBToYUV444_8u_P3AC4R || !prims->YUV444ToRGB_8u_P3AC4R)
476 return FALSE;
477 }
478 else
479 {
480 uvwidth = (awidth + 1) / 2;
481 uvsize = (aheight + 1) / 2 * uvwidth;
482
483 if (!prims || !prims->RGBToYUV420_8u_P3AC4R || !prims->YUV420ToRGB_8u_P3AC4R)
484 return FALSE;
485 }
486
487 (void)fprintf(stderr, "Running AVC%s on frame size %" PRIu32 "x%" PRIu32 "\n",
488 use444 ? "444" : "420", roi.width, roi.height);
489
490 /* Test RGB to YUV444 conversion and vice versa */
491 if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
492 goto fail;
493
494 if (!(rgb_dst = set_padding(size * sizeof(UINT32), padding)))
495 goto fail;
496
497 if (!(yuv[0] = set_padding(size, padding)))
498 goto fail;
499
500 if (!(yuv[1] = set_padding(uvsize, padding)))
501 goto fail;
502
503 if (!(yuv[2] = set_padding(uvsize, padding)))
504 goto fail;
505
506 for (size_t y = 0; y < roi.height; y++)
507 {
508 BYTE* line = &rgb[y * stride];
509 if (winpr_RAND(line, stride) < 0)
510 goto fail;
511 }
512
513 yuv_step[0] = awidth;
514 yuv_step[1] = uvwidth;
515 yuv_step[2] = uvwidth;
516
517 for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
518 {
519 pstatus_t rc = 0;
520 const UINT32 DstFormat = formats[x];
521 printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
522 memset(rgb_dst, PADDING_FILL_VALUE, size * sizeof(UINT32));
523
524 PROFILER_CREATE(rgbToYUV420, "RGBToYUV420")
525 PROFILER_CREATE(rgbToYUV444, "RGBToYUV444")
526 PROFILER_CREATE(yuv420ToRGB, "YUV420ToRGB")
527 PROFILER_CREATE(yuv444ToRGB, "YUV444ToRGB")
528
529 if (use444)
530 {
531 PROFILER_ENTER(rgbToYUV444)
532 rc = prims->RGBToYUV444_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
533 PROFILER_EXIT(rgbToYUV444)
534
535 if (rc != PRIMITIVES_SUCCESS)
536 goto loop_fail;
537
538 PROFILER_PRINT_HEADER
539 PROFILER_PRINT(rgbToYUV444)
540 PROFILER_PRINT_FOOTER
541 }
542 else
543 {
544 PROFILER_ENTER(rgbToYUV420)
545 rc = prims->RGBToYUV420_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
546 PROFILER_EXIT(rgbToYUV420)
547
548 if (rc != PRIMITIVES_SUCCESS)
549 goto loop_fail;
550
551 PROFILER_PRINT_HEADER
552 PROFILER_PRINT(rgbToYUV420)
553 PROFILER_PRINT_FOOTER
554 }
555
556 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
557 {
558 rc = -1;
559 goto loop_fail;
560 }
561
562 if ((!check_padding(yuv[0], size, padding, "Y")) ||
563 (!check_padding(yuv[1], uvsize, padding, "U")) ||
564 (!check_padding(yuv[2], uvsize, padding, "V")))
565 {
566 rc = -1;
567 goto loop_fail;
568 }
569
570 cnv.pv = yuv;
571 if (use444)
572 {
573 PROFILER_ENTER(yuv444ToRGB)
574 rc = prims->YUV444ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi);
575 PROFILER_EXIT(yuv444ToRGB)
576
577 if (rc != PRIMITIVES_SUCCESS)
578 goto loop_fail;
579
580 loop_fail:
581 PROFILER_EXIT(yuv444ToRGB)
582 PROFILER_PRINT_HEADER
583 PROFILER_PRINT(yuv444ToRGB)
584 PROFILER_PRINT_FOOTER
585
586 if (rc != PRIMITIVES_SUCCESS)
587 goto fail;
588 }
589 else
590 {
591 PROFILER_ENTER(yuv420ToRGB)
592
593 if (prims->YUV420ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi) !=
594 PRIMITIVES_SUCCESS)
595 {
596 PROFILER_EXIT(yuv420ToRGB)
597 goto fail;
598 }
599
600 PROFILER_EXIT(yuv420ToRGB)
601 PROFILER_PRINT_HEADER
602 PROFILER_PRINT(yuv420ToRGB)
603 PROFILER_PRINT_FOOTER
604 }
605
606 if (!check_padding(rgb_dst, size * sizeof(UINT32), padding, "rgb dst"))
607 goto fail;
608
609 if ((!check_padding(yuv[0], size, padding, "Y")) ||
610 (!check_padding(yuv[1], uvsize, padding, "U")) ||
611 (!check_padding(yuv[2], uvsize, padding, "V")))
612 goto fail;
613
614 BOOL equal = TRUE;
615 for (size_t y = 0; y < roi.height; y++)
616 {
617 BYTE* srgb = &rgb[y * stride];
618 BYTE* drgb = &rgb_dst[y * stride];
619
620 if (!similarRGB(y, srgb, drgb, roi.width, DstFormat, use444))
621 equal = FALSE;
622 }
623 if (!equal)
624 goto fail;
625
626 PROFILER_FREE(rgbToYUV420)
627 PROFILER_FREE(rgbToYUV444)
628 PROFILER_FREE(yuv420ToRGB)
629 PROFILER_FREE(yuv444ToRGB)
630 }
631
632 res = TRUE;
633fail:
634 printf("[%s] run %s.\n", __func__, (res) ? "SUCCESS" : "FAILED");
635 free_padding(rgb, padding);
636 free_padding(rgb_dst, padding);
637 free_padding(yuv[0], padding);
638 free_padding(yuv[1], padding);
639 free_padding(yuv[2], padding);
640 return res;
641}
642
643static BOOL allocate_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
644{
645 const size_t size = 1ULL * width * height;
646 const size_t uvwidth = (1ULL + width) / 2;
647 const size_t uvsize = (1ULL + height) / 2 * uvwidth;
648
649 if (!(planes[0] = set_padding(size, padding)))
650 goto fail;
651
652 if (!(planes[1] = set_padding(uvsize, padding)))
653 goto fail;
654
655 if (!(planes[2] = set_padding(uvsize, padding)))
656 goto fail;
657
658 return TRUE;
659fail:
660 free_padding(planes[0], padding);
661 free_padding(planes[1], padding);
662 free_padding(planes[2], padding);
663 return FALSE;
664}
665
666static void free_yuv420(BYTE** planes, UINT32 padding)
667{
668 if (!planes)
669 return;
670
671 free_padding(planes[0], padding);
672 free_padding(planes[1], padding);
673 free_padding(planes[2], padding);
674 planes[0] = nullptr;
675 planes[1] = nullptr;
676 planes[2] = nullptr;
677}
678
679static BOOL check_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
680{
681 const size_t size = 1ULL * width * height;
682 const size_t uvwidth = (width + 1) / 2;
683 const size_t uvsize = (height + 1) / 2 * uvwidth;
684 const BOOL yOk = check_padding(planes[0], size, padding, "Y");
685 const BOOL uOk = check_padding(planes[1], uvsize, padding, "U");
686 const BOOL vOk = check_padding(planes[2], uvsize, padding, "V");
687 return (yOk && uOk && vOk);
688}
689
690static BOOL check_for_mismatches(const BYTE* planeA, const BYTE* planeB, UINT32 size)
691{
692 BOOL rc = FALSE;
693
694 for (UINT32 x = 0; x < size; x++)
695 {
696 const BYTE a = planeA[x];
697 const BYTE b = planeB[x];
698
699 if (fabsf((float)a - (float)b) > 2.0f)
700 {
701 rc = TRUE;
702 (void)fprintf(stderr, "[%08x] %02x != %02x\n", x, a, b);
703 }
704 }
705
706 return rc;
707}
708
709static BOOL compare_yuv420(BYTE** planesA, BYTE** planesB, UINT32 width, UINT32 height,
710 UINT32 padding)
711{
712 BOOL rc = TRUE;
713 const size_t size = 1ULL * width * height;
714 const size_t uvwidth = (1ULL * width + 1) / 2;
715 const size_t uvsize = (1ULL * height + 1) / 2 * uvwidth;
716
717 if (check_for_mismatches(planesA[0], planesB[0], size))
718 {
719 (void)fprintf(stderr, "Mismatch in Y planes!\n");
720 rc = FALSE;
721 }
722
723 if (check_for_mismatches(planesA[1], planesB[1], uvsize))
724 {
725 (void)fprintf(stderr, "Mismatch in U planes!\n");
726 rc = FALSE;
727 }
728
729 if (check_for_mismatches(planesA[2], planesB[2], uvsize))
730 {
731 (void)fprintf(stderr, "Mismatch in V planes!\n");
732 rc = FALSE;
733 }
734
735 return rc;
736}
737
738static UINT32 prand(UINT32 max)
739{
740 UINT32 tmp = 0;
741 if (max <= 1)
742 return 1;
743 if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
744 {
745 (void)fprintf(stderr, "winpr_RAND failed, retry...\n");
746 // NOLINTNEXTLINE(concurrency-mt-unsafe)
747 exit(-1);
748 }
749 return tmp % (max - 1) + 1;
750}
751
752static BOOL TestPrimitiveRgbToLumaChroma(primitives_t* prims, prim_size_t roi, UINT32 version)
753{
754 BOOL res = FALSE;
755 UINT32 awidth = 0;
756 UINT32 aheight = 0;
757 BYTE* luma[3] = WINPR_C_ARRAY_INIT;
758 BYTE* chroma[3] = WINPR_C_ARRAY_INIT;
759 BYTE* lumaGeneric[3] = WINPR_C_ARRAY_INIT;
760 BYTE* chromaGeneric[3] = WINPR_C_ARRAY_INIT;
761 UINT32 yuv_step[3];
762 BYTE* rgb = nullptr;
763 size_t size = 0;
764 size_t uvwidth = 0;
765 const size_t padding = 0x1000;
766 UINT32 stride = 0;
767 fn_RGBToAVC444YUV_t fkt = nullptr;
768 fn_RGBToAVC444YUV_t gen = nullptr;
769 const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
770 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
771 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
772 PROFILER_DEFINE(rgbToYUV444)
773 PROFILER_DEFINE(rgbToYUV444opt)
774 /* Buffers need to be 16x16 aligned. */
775 awidth = roi.width;
776
777 if (awidth % 16 != 0)
778 awidth += 16 - roi.width % 16;
779
780 aheight = roi.height;
781
782 if (aheight % 16 != 0)
783 aheight += 16 - roi.height % 16;
784
785 stride = 1ULL * awidth * sizeof(UINT32);
786 size = 1ULL * awidth * aheight;
787 uvwidth = 1ULL * (awidth + 1) / 2;
788
789 if (!prims || !generic)
790 return FALSE;
791
792 switch (version)
793 {
794 case 1:
795 fkt = prims->RGBToAVC444YUV;
796 gen = generic->RGBToAVC444YUV;
797 break;
798
799 case 2:
800 fkt = prims->RGBToAVC444YUVv2;
801 gen = generic->RGBToAVC444YUVv2;
802 break;
803
804 default:
805 return FALSE;
806 }
807
808 if (!fkt || !gen)
809 return FALSE;
810
811 (void)fprintf(stderr, "Running AVC444 on frame size %" PRIu32 "x%" PRIu32 "\n", roi.width,
812 roi.height);
813
814 /* Test RGB to YUV444 conversion and vice versa */
815 if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
816 goto fail;
817
818 if (!allocate_yuv420(luma, awidth, aheight, padding))
819 goto fail;
820
821 if (!allocate_yuv420(chroma, awidth, aheight, padding))
822 goto fail;
823
824 if (!allocate_yuv420(lumaGeneric, awidth, aheight, padding))
825 goto fail;
826
827 if (!allocate_yuv420(chromaGeneric, awidth, aheight, padding))
828 goto fail;
829
830 for (size_t y = 0; y < roi.height; y++)
831 {
832 BYTE* line = &rgb[y * stride];
833
834 if (winpr_RAND(line, 4ULL * roi.width) < 0)
835 goto fail;
836 }
837
838 yuv_step[0] = awidth;
839 yuv_step[1] = uvwidth;
840 yuv_step[2] = uvwidth;
841
842 for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
843 {
844 pstatus_t rc = -1;
845 const UINT32 DstFormat = formats[x];
846 printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
847 PROFILER_CREATE(rgbToYUV444, "RGBToYUV444-generic")
848 PROFILER_CREATE(rgbToYUV444opt, "RGBToYUV444-optimized")
849
850 for (UINT32 cnt = 0; cnt < 10; cnt++)
851 {
852 PROFILER_ENTER(rgbToYUV444opt)
853 rc = fkt(rgb, DstFormat, stride, luma, yuv_step, chroma, yuv_step, &roi);
854 PROFILER_EXIT(rgbToYUV444opt)
855
856 if (rc != PRIMITIVES_SUCCESS)
857 goto loop_fail;
858 }
859
860 PROFILER_PRINT_HEADER
861 PROFILER_PRINT(rgbToYUV444opt)
862 PROFILER_PRINT_FOOTER
863
864 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
865 {
866 rc = -1;
867 goto loop_fail;
868 }
869
870 if (!check_yuv420(luma, awidth, aheight, padding) ||
871 !check_yuv420(chroma, awidth, aheight, padding))
872 {
873 rc = -1;
874 goto loop_fail;
875 }
876
877 for (UINT32 cnt = 0; cnt < 10; cnt++)
878 {
879 PROFILER_ENTER(rgbToYUV444)
880 rc = gen(rgb, DstFormat, stride, lumaGeneric, yuv_step, chromaGeneric, yuv_step, &roi);
881 PROFILER_EXIT(rgbToYUV444)
882
883 if (rc != PRIMITIVES_SUCCESS)
884 goto loop_fail;
885 }
886
887 PROFILER_PRINT_HEADER
888 PROFILER_PRINT(rgbToYUV444)
889 PROFILER_PRINT_FOOTER
890
891 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
892 {
893 rc = -1;
894 goto loop_fail;
895 }
896
897 if (!check_yuv420(lumaGeneric, awidth, aheight, padding) ||
898 !check_yuv420(chromaGeneric, awidth, aheight, padding))
899 {
900 rc = -1;
901 goto loop_fail;
902 }
903
904 if (!compare_yuv420(luma, lumaGeneric, awidth, aheight, padding) ||
905 !compare_yuv420(chroma, chromaGeneric, awidth, aheight, padding))
906 {
907 rc = -1;
908 goto loop_fail;
909 }
910
911 loop_fail:
912 PROFILER_FREE(rgbToYUV444)
913 PROFILER_FREE(rgbToYUV444opt)
914
915 if (rc != PRIMITIVES_SUCCESS)
916 goto fail;
917 }
918
919 res = TRUE;
920fail:
921 printf("[%s][version %u] run %s.\n", __func__, (unsigned)version, (res) ? "SUCCESS" : "FAILED");
922 free_padding(rgb, padding);
923 free_yuv420(luma, padding);
924 free_yuv420(chroma, padding);
925 free_yuv420(lumaGeneric, padding);
926 free_yuv420(chromaGeneric, padding);
927 return res;
928}
929
930static BOOL run_tests(prim_size_t roi)
931{
932 BOOL rc = FALSE;
933 for (UINT32 type = PRIMITIVES_PURE_SOFT; type <= PRIMITIVES_AUTODETECT; type++)
934 {
935 primitives_t* prims = primitives_get_by_type(type);
936 if (!prims)
937 {
938 printf("primitives type %d not supported\n", type);
939 continue;
940 }
941
942 for (UINT32 x = 0; x < 5; x++)
943 {
944
945 printf("-------------------- GENERIC ------------------------\n");
946
947 if (!TestPrimitiveYUV(prims, roi, TRUE))
948 goto fail;
949
950 printf("---------------------- END --------------------------\n");
951 printf("-------------------- GENERIC ------------------------\n");
952
953 if (!TestPrimitiveYUV(prims, roi, FALSE))
954 goto fail;
955
956 printf("---------------------- END --------------------------\n");
957 printf("-------------------- GENERIC ------------------------\n");
958
959 if (!TestPrimitiveYUVCombine(prims, roi))
960 goto fail;
961
962 printf("---------------------- END --------------------------\n");
963 printf("-------------------- GENERIC ------------------------\n");
964
965 if (!TestPrimitiveRgbToLumaChroma(prims, roi, 1))
966 goto fail;
967
968 printf("---------------------- END --------------------------\n");
969 printf("-------------------- GENERIC ------------------------\n");
970
971 if (!TestPrimitiveRgbToLumaChroma(prims, roi, 2))
972 goto fail;
973
974 printf("---------------------- END --------------------------\n");
975 }
976 }
977 rc = TRUE;
978fail:
979 printf("[%s] run %s.\n", __func__, (rc) ? "SUCCESS" : "FAILED");
980 return rc;
981}
982
983static void free_yuv(BYTE* yuv[3])
984{
985 for (size_t x = 0; x < 3; x++)
986 {
987 free(yuv[x]);
988 yuv[x] = nullptr;
989 }
990}
991
992static BOOL allocate_yuv(BYTE* yuv[3], prim_size_t roi)
993{
994 yuv[0] = calloc(roi.width, roi.height);
995 yuv[1] = calloc(roi.width, roi.height);
996 yuv[2] = calloc(roi.width, roi.height);
997
998 if (!yuv[0] || !yuv[1] || !yuv[2])
999 {
1000 free_yuv(yuv);
1001 return FALSE;
1002 }
1003
1004 if (winpr_RAND(yuv[0], 1ULL * roi.width * roi.height) < 0)
1005 return FALSE;
1006 if (winpr_RAND(yuv[1], 1ULL * roi.width * roi.height) < 0)
1007 return FALSE;
1008 if (winpr_RAND(yuv[2], 1ULL * roi.width * roi.height) < 0)
1009 return FALSE;
1010 return TRUE;
1011}
1012
1013static BOOL yuv444_to_rgb(BYTE* rgb, size_t stride, const BYTE* yuv[3], const UINT32 yuvStep[3],
1014 prim_size_t roi)
1015{
1016 for (size_t y = 0; y < roi.height; y++)
1017 {
1018 const BYTE* yline[3] = {
1019 yuv[0] + y * roi.width,
1020 yuv[1] + y * roi.width,
1021 yuv[2] + y * roi.width,
1022 };
1023 BYTE* line = &rgb[y * stride];
1024
1025 for (size_t x = 0; x < roi.width; x++)
1026 {
1027 const BYTE Y = yline[0][x];
1028 const BYTE U = yline[1][x];
1029 const BYTE V = yline[2][x];
1030
1031 writeYUVPixel(&line[x * 4], PIXEL_FORMAT_BGRX32, Y, U, V, writePixelBGRX);
1032 }
1033 }
1034 return TRUE;
1035}
1036
1037/* Check the result of generic matches the optimized routine.
1038 *
1039 */
1040static BOOL compare_yuv444_to_rgb(prim_size_t roi, DWORD type)
1041{
1042 BOOL rc = FALSE;
1043 const UINT32 format = PIXEL_FORMAT_BGRA32;
1044 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1045 const UINT32 yuvStep[3] = { roi.width, roi.width, roi.width };
1046 const size_t stride = 4ULL * roi.width;
1047
1048 primitives_t* prims = primitives_get_by_type(type);
1049 if (!prims)
1050 {
1051 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1052 return TRUE;
1053 }
1054
1055 BYTE* rgb1 = calloc(roi.height, stride);
1056 BYTE* rgb2 = calloc(roi.height, stride);
1057
1058 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1059 if (!soft)
1060 goto fail;
1061 if (!allocate_yuv(yuv, roi) || !rgb1 || !rgb2)
1062 goto fail;
1063
1064 const BYTE* cyuv[] = { yuv[0], yuv[1], yuv[2] };
1065 if (soft->YUV444ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb1, stride, format, &roi) !=
1066 PRIMITIVES_SUCCESS)
1067 goto fail;
1068 if (prims->YUV444ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb2, stride, format, &roi) !=
1069 PRIMITIVES_SUCCESS)
1070 goto fail;
1071
1072 for (size_t y = 0; y < roi.height; y++)
1073 {
1074 const BYTE* yline[3] = {
1075 yuv[0] + y * roi.width,
1076 yuv[1] + y * roi.width,
1077 yuv[2] + y * roi.width,
1078 };
1079 const BYTE* line1 = &rgb1[y * stride];
1080 const BYTE* line2 = &rgb2[y * stride];
1081
1082 for (size_t x = 0; x < roi.width; x++)
1083 {
1084 const int Y = yline[0][x];
1085 const int U = yline[1][x];
1086 const int V = yline[2][x];
1087 const UINT32 color1 = FreeRDPReadColor(&line1[x * 4], format);
1088 const UINT32 color2 = FreeRDPReadColor(&line2[x * 4], format);
1089
1090 BYTE r1 = 0;
1091 BYTE g1 = 0;
1092 BYTE b1 = 0;
1093 FreeRDPSplitColor(color1, format, &r1, &g1, &b1, nullptr, nullptr);
1094
1095 BYTE r2 = 0;
1096 BYTE g2 = 0;
1097 BYTE b2 = 0;
1098 FreeRDPSplitColor(color2, format, &r2, &g2, &b2, nullptr, nullptr);
1099
1100 const int dr12 = abs(r1 - r2);
1101 const int dg12 = abs(g1 - g2);
1102 const int db12 = abs(b1 - b2);
1103
1104 if ((dr12 != 0) || (dg12 != 0) || (db12 != 0))
1105 {
1106 printf("{\n");
1107 printf("\tdiff 1/2: yuv {%d, %d, %d}, rgb {%d, %d, %d}\n", Y, U, V, dr12, dg12,
1108 db12);
1109 printf("}\n");
1110 }
1111
1112 if ((dr12 > 0) || (dg12 > 0) || (db12 > 0))
1113 {
1114 (void)fprintf(stderr,
1115 "[%" PRIuz "x%" PRIuz
1116 "] generic and optimized data mismatch: r[0x%" PRIx8 "|0x%" PRIx8
1117 "] g[0x%" PRIx8 "|0x%" PRIx8 "] b[0x%" PRIx8 "|0x%" PRIx8 "]\n",
1118 x, y, r1, r2, g1, g2, b1, b2);
1119 (void)fprintf(stderr, "roi: %dx%d\n", roi.width, roi.height);
1120 winpr_HexDump("y0", WLOG_INFO, &yline[0][x], 16);
1121 winpr_HexDump("y1", WLOG_INFO, &yline[0][x + roi.width], 16);
1122 winpr_HexDump("u0", WLOG_INFO, &yline[1][x], 16);
1123 winpr_HexDump("u1", WLOG_INFO, &yline[1][x + roi.width], 16);
1124 winpr_HexDump("v0", WLOG_INFO, &yline[2][x], 16);
1125 winpr_HexDump("v1", WLOG_INFO, &yline[2][x + roi.width], 16);
1126 winpr_HexDump("foo1", WLOG_INFO, &line1[x * 4], 16);
1127 winpr_HexDump("foo2", WLOG_INFO, &line2[x * 4], 16);
1128 goto fail;
1129 }
1130 }
1131 }
1132
1133 rc = TRUE;
1134fail:
1135 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1136 free_yuv(yuv);
1137 free(rgb1);
1138 free(rgb2);
1139
1140 return rc;
1141}
1142
1143/* Check I444ToRGB_8u converts every pixel on its own (no AVC444 chroma
1144 * filter is reversed) and does not write past the end of a line.
1145 */
1146static BOOL compare_i444_to_rgb(prim_size_t roi, DWORD type)
1147{
1148 BOOL rc = FALSE;
1149 const UINT32 format = PIXEL_FORMAT_BGRX32;
1150 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1151 const UINT32 yuvStep[3] = { roi.width, roi.width, roi.width };
1152 const size_t padding = 64;
1153 const size_t stride = 4ULL * roi.width + padding;
1154
1155 primitives_t* prims = primitives_get_by_type(type);
1156 if (!prims)
1157 {
1158 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1159 return TRUE;
1160 }
1161
1162 BYTE* expected = calloc(roi.height, stride);
1163 BYTE* rgb = calloc(roi.height, stride);
1164 if (!expected || !rgb || !prims->I444ToRGB_8u)
1165 goto fail;
1166 if (!allocate_yuv(yuv, roi))
1167 goto fail;
1168
1169 memset(expected, PADDING_FILL_VALUE, roi.height * stride);
1170 memset(rgb, PADDING_FILL_VALUE, roi.height * stride);
1171
1172 const BYTE* cyuv[] = { yuv[0], yuv[1], yuv[2] };
1173 if (!yuv444_to_rgb(expected, stride, cyuv, yuvStep, roi))
1174 goto fail;
1175 if (prims->I444ToRGB_8u(cyuv, yuvStep, rgb, stride, format, &roi) != PRIMITIVES_SUCCESS)
1176 goto fail;
1177
1178 for (size_t y = 0; y < roi.height; y++)
1179 {
1180 if (memcmp(&expected[y * stride], &rgb[y * stride], stride) != 0)
1181 {
1182 (void)fprintf(stderr, "[%s] [%" PRIu32 "x%" PRIu32 "] mismatch in line %" PRIuz "\n",
1183 __func__, roi.width, roi.height, y);
1184 goto fail;
1185 }
1186 }
1187
1188 rc = TRUE;
1189fail:
1190 printf("%s [%" PRIu32 "x%" PRIu32 "] finished with %s\n", __func__, roi.width, roi.height,
1191 rc ? "SUCCESS" : "FAILURE");
1192 free_yuv(yuv);
1193 free(expected);
1194 free(rgb);
1195
1196 return rc;
1197}
1198
1199/* Check RGBToI444_8u against a per pixel reference: every pixel decoded on its
1200 * own with FreeRDPReadColor and converted with the RGB2Y/RGB2U/RGB2V formula,
1201 * for each 32 bit format and a 24 bit one, with padded source and destination
1202 * strides. The vector routines must match it byte for byte, including the
1203 * scalar tail of a width that is not a multiple of the vector width. */
1204static BOOL compare_rgb_to_i444(prim_size_t roi, DWORD type)
1205{
1206 BOOL rc = FALSE;
1207 const UINT32 formats[] = { PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBA32,
1208 PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XRGB32,
1209 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_BGR24 };
1210 const size_t srcStep = 4ULL * roi.width + 12;
1211 const UINT32 dstStep[3] = { roi.width + 5, roi.width + 7, roi.width + 9 };
1212 BYTE* rgb = nullptr;
1213 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1214 BYTE* ref[3] = WINPR_C_ARRAY_INIT;
1215
1216 primitives_t* prims = primitives_get_by_type(type);
1217 if (!prims)
1218 {
1219 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1220 return TRUE;
1221 }
1222 if (!prims->RGBToI444_8u)
1223 goto fail;
1224
1225 rgb = calloc(roi.height, srcStep);
1226 if (!rgb)
1227 goto fail;
1228 for (size_t x = 0; x < 3; x++)
1229 {
1230 yuv[x] = calloc(roi.height, dstStep[x]);
1231 ref[x] = calloc(roi.height, dstStep[x]);
1232 if (!yuv[x] || !ref[x])
1233 goto fail;
1234 }
1235
1236 if (winpr_RAND(rgb, roi.height * srcStep) < 0)
1237 goto fail;
1238 /* the corners of the colour cube first: the extremes of every formula */
1239 for (size_t x = 0; x < MIN(32ULL, roi.width * 4ULL); x++)
1240 rgb[x] = ((x >> (x % 4)) & 1) ? 0xFF : 0x00;
1241
1242 for (size_t f = 0; f < ARRAYSIZE(formats); f++)
1243 {
1244 const UINT32 format = formats[f];
1245 const size_t bpp = FreeRDPGetBytesPerPixel(format);
1246
1247 for (size_t y = 0; y < roi.height; y++)
1248 {
1249 for (size_t x = 0; x < roi.width; x++)
1250 {
1251 BYTE R = 0;
1252 BYTE G = 0;
1253 BYTE B = 0;
1254 const UINT32 color = FreeRDPReadColor(&rgb[y * srcStep + x * bpp], format);
1255 FreeRDPSplitColor(color, format, &R, &G, &B, nullptr, nullptr);
1256 ref[0][y * dstStep[0] + x] = (BYTE)((54 * R + 183 * G + 18 * B) >> 8);
1257 ref[1][y * dstStep[1] + x] = (BYTE)(((-29 * R - 99 * G + 128 * B) >> 8) + 128);
1258 ref[2][y * dstStep[2] + x] = (BYTE)(((128 * R - 116 * G - 12 * B) >> 8) + 128);
1259 }
1260 }
1261
1262 for (size_t x = 0; x < 3; x++)
1263 memset(yuv[x], PADDING_FILL_VALUE, 1ull * roi.height * dstStep[x]);
1264
1265 if (prims->RGBToI444_8u(rgb, format, (UINT32)srcStep, yuv, dstStep, &roi) !=
1266 PRIMITIVES_SUCCESS)
1267 goto fail;
1268
1269 for (size_t y = 0; y < roi.height; y++)
1270 {
1271 for (size_t x = 0; x < 3; x++)
1272 {
1273 const BYTE* line = &yuv[x][y * dstStep[x]];
1274 const BYTE* expect = &ref[x][y * dstStep[x]];
1275 if (memcmp(line, expect, roi.width) != 0)
1276 {
1277 (void)fprintf(stderr,
1278 "[%s] %s %" PRIu32 "x%" PRIu32 ": plane %" PRIuz
1279 " differs in line %" PRIuz "\n",
1280 __func__, FreeRDPGetColorFormatName(format), roi.width,
1281 roi.height, x, y);
1282 goto fail;
1283 }
1284 /* nothing written past the width */
1285 for (size_t p = roi.width; p < dstStep[x]; p++)
1286 {
1287 if (line[p] != PADDING_FILL_VALUE)
1288 {
1289 (void)fprintf(stderr, "[%s] %s: plane %" PRIuz " overwrote padding\n",
1290 __func__, FreeRDPGetColorFormatName(format), x);
1291 goto fail;
1292 }
1293 }
1294 }
1295 }
1296 }
1297
1298 rc = TRUE;
1299fail:
1300 printf("%s %" PRIu32 "x%" PRIu32 " finished with %s\n", __func__, roi.width, roi.height,
1301 rc ? "SUCCESS" : "FAILURE");
1302 free(rgb);
1303 for (size_t x = 0; x < 3; x++)
1304 {
1305 free(yuv[x]);
1306 free(ref[x]);
1307 }
1308 return rc;
1309}
1310
1311/* Check the result of generic matches the optimized routine.
1312 *
1313 */
1314static BOOL compare_rgb_to_yuv444(prim_size_t roi, DWORD type)
1315{
1316 BOOL rc = FALSE;
1317 const UINT32 format = PIXEL_FORMAT_BGRA32;
1318 const size_t stride = 4ULL * roi.width;
1319 const UINT32 yuvStep[] = { roi.width, roi.width, roi.width };
1320 BYTE* yuv1[3] = WINPR_C_ARRAY_INIT;
1321 BYTE* yuv2[3] = WINPR_C_ARRAY_INIT;
1322
1323 primitives_t* prims = primitives_get_by_type(type);
1324 if (!prims)
1325 {
1326 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1327 return TRUE;
1328 }
1329
1330 BYTE* rgb = calloc(roi.height, stride);
1331
1332 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1333 if (!soft || !rgb)
1334 goto fail;
1335
1336 if (!allocate_yuv(yuv1, roi) || !allocate_yuv(yuv2, roi))
1337 goto fail;
1338
1339 if (soft->RGBToYUV444_8u_P3AC4R(rgb, format, stride, yuv1, yuvStep, &roi) != PRIMITIVES_SUCCESS)
1340 goto fail;
1341 if (prims->RGBToYUV444_8u_P3AC4R(rgb, format, stride, yuv2, yuvStep, &roi) !=
1342 PRIMITIVES_SUCCESS)
1343 goto fail;
1344
1345 for (size_t y = 0; y < roi.height; y++)
1346 {
1347 const BYTE* yline1[3] = {
1348 yuv1[0] + y * roi.width,
1349 yuv1[1] + y * roi.width,
1350 yuv1[2] + y * roi.width,
1351 };
1352 const BYTE* yline2[3] = {
1353 yuv2[0] + y * roi.width,
1354 yuv2[1] + y * roi.width,
1355 yuv2[2] + y * roi.width,
1356 };
1357
1358 for (size_t x = 0; x < ARRAYSIZE(yline1); x++)
1359 {
1360 if (memcmp(yline1[x], yline2[x], yuvStep[x]) != 0)
1361 {
1362 (void)fprintf(stderr, "[%s] compare failed in line %" PRIuz, __func__, x);
1363 goto fail;
1364 }
1365 }
1366 }
1367
1368 rc = TRUE;
1369fail:
1370 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1371 free(rgb);
1372 free_yuv(yuv1);
1373 free_yuv(yuv2);
1374
1375 return rc;
1376}
1377
1378/* Check the result of generic matches the optimized routine.
1379 *
1380 */
1381static BOOL compare_yuv420_to_rgb(prim_size_t roi, DWORD type)
1382{
1383 BOOL rc = FALSE;
1384 const UINT32 format = PIXEL_FORMAT_BGRA32;
1385 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1386 const UINT32 yuvStep[3] = { roi.width, roi.width / 2, roi.width / 2 };
1387 const size_t stride = 4ULL * roi.width;
1388
1389 primitives_t* prims = primitives_get_by_type(type);
1390 if (!prims)
1391 {
1392 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1393 return TRUE;
1394 }
1395
1396 BYTE* rgb1 = calloc(roi.height, stride);
1397 BYTE* rgb2 = calloc(roi.height, stride);
1398
1399 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1400 if (!soft)
1401 goto fail;
1402 if (!allocate_yuv(yuv, roi) || !rgb1 || !rgb2)
1403 goto fail;
1404
1405 const BYTE* cyuv[3] = { yuv[0], yuv[1], yuv[2] };
1406 if (soft->YUV420ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb1, stride, format, &roi) !=
1407 PRIMITIVES_SUCCESS)
1408 goto fail;
1409 if (prims->YUV420ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb2, stride, format, &roi) !=
1410 PRIMITIVES_SUCCESS)
1411 goto fail;
1412
1413 for (size_t y = 0; y < roi.height; y++)
1414 {
1415 const BYTE* yline[3] = {
1416 yuv[0] + y * yuvStep[0],
1417 yuv[1] + y * yuvStep[1],
1418 yuv[2] + y * yuvStep[2],
1419 };
1420 const BYTE* line1 = &rgb1[y * stride];
1421 const BYTE* line2 = &rgb2[y * stride];
1422
1423 for (size_t x = 0; x < roi.width; x++)
1424 {
1425 const int Y = yline[0][x];
1426 const int U = yline[1][x / 2];
1427 const int V = yline[2][x / 2];
1428 const UINT32 color1 = FreeRDPReadColor(&line1[x * 4], format);
1429 const UINT32 color2 = FreeRDPReadColor(&line2[x * 4], format);
1430
1431 BYTE r1 = 0;
1432 BYTE g1 = 0;
1433 BYTE b1 = 0;
1434 FreeRDPSplitColor(color1, format, &r1, &g1, &b1, nullptr, nullptr);
1435
1436 BYTE r2 = 0;
1437 BYTE g2 = 0;
1438 BYTE b2 = 0;
1439 FreeRDPSplitColor(color2, format, &r2, &g2, &b2, nullptr, nullptr);
1440
1441 const int dr12 = abs(r1 - r2);
1442 const int dg12 = abs(g1 - g2);
1443 const int db12 = abs(b1 - b2);
1444
1445 if ((dr12 != 0) || (dg12 != 0) || (db12 != 0))
1446 {
1447 printf("{\n");
1448 printf("\tdiff 1/2: yuv {%d, %d, %d}, rgb {%d, %d, %d}\n", Y, U, V, dr12, dg12,
1449 db12);
1450 printf("}\n");
1451 }
1452
1453 if ((dr12 > 0) || (dg12 > 0) || (db12 > 0))
1454 {
1455 printf("[%s] failed: r[%" PRIx8 "|%" PRIx8 "] g[%" PRIx8 "|%" PRIx8 "] b[%" PRIx8
1456 "|%" PRIx8 "]\n",
1457 __func__, r1, r2, g1, g2, b1, b2);
1458 goto fail;
1459 }
1460 }
1461 }
1462
1463 rc = TRUE;
1464fail:
1465 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1466 free_yuv(yuv);
1467 free(rgb1);
1468 free(rgb2);
1469
1470 return rc;
1471}
1472
1473static BOOL similarYUV(const BYTE* line1, const BYTE* line2, size_t len)
1474{
1475 for (size_t x = 0; x < len; x++)
1476 {
1477 const int a = line1[x];
1478 const int b = line2[x];
1479 const int diff = abs(a - b);
1480 if (diff >= 2)
1481 return FALSE;
1482 }
1483 return TRUE;
1484}
1485
1486/* Due to optimizations the Y value might be off by +/- 1 */
1487static int similarY(const BYTE* a, const BYTE* b, size_t size, size_t type)
1488{
1489 switch (type)
1490 {
1491 case 0:
1492 case 1:
1493 case 2:
1494 for (size_t x = 0; x < size; x++)
1495 {
1496 const int ba = a[x];
1497 const int bb = b[x];
1498 const int diff = abs(ba - bb);
1499 if (diff > 2)
1500 return diff;
1501 }
1502 return 0;
1503 break;
1504 default:
1505 return memcmp(a, b, size);
1506 }
1507}
1508/* Check the result of generic matches the optimized routine.
1509 *
1510 */
1511/* How far an optimized RGB to YUV conversion may be from the generic one: the SSE4.1
1512 * routines use 7 and 8 bit coefficients, which puts Y up to 1 and U and V up to 2 off;
1513 * the NEON ones keep the generic integer math and match it. */
1514#define YUV_ENCODE_TOLERANCE 2
1515
1516static BOOL yuv_encode_close(const BYTE* a, const BYTE* b, size_t len)
1517{
1518 for (size_t x = 0; x < len; x++)
1519 {
1520 if (abs((int)a[x] - (int)b[x]) > YUV_ENCODE_TOLERANCE)
1521 return FALSE;
1522 }
1523 return TRUE;
1524}
1525
1526/* RGBToYUV420_8u_P3AC4R against the generic routine at odd widths and heights, with padded
1527 * strides, for the formats it handles and one it hands back to the generic routine: the
1528 * values within YUV_ENCODE_TOLERANCE, and nothing written into the padding. */
1529static BOOL compare_rgb_to_yuv420_edges(prim_size_t roi, DWORD type)
1530{
1531 BOOL rc = FALSE;
1532 const UINT32 formats[] = { PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBA32,
1533 PIXEL_FORMAT_RGBX32, PIXEL_FORMAT_ARGB32 };
1534 const size_t srcStep = 4ULL * roi.width + 12;
1535 const UINT32 cw = (roi.width + 1) / 2;
1536 const UINT32 ch = (roi.height + 1) / 2;
1537 const UINT32 dstStep[3] = { roi.width + 5, cw + 7, cw + 9 };
1538 const UINT32 rows[3] = { roi.height, ch, ch };
1539 const UINT32 used[3] = { roi.width, cw, cw };
1540 BYTE* rgb = nullptr;
1541 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1542 BYTE* ref[3] = WINPR_C_ARRAY_INIT;
1543
1544 primitives_t* prims = primitives_get_by_type(type);
1545 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1546 if (!prims)
1547 {
1548 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1549 return TRUE;
1550 }
1551 if (!soft)
1552 goto fail;
1553
1554 rgb = calloc(roi.height, srcStep);
1555 if (!rgb)
1556 goto fail;
1557 for (size_t x = 0; x < 3; x++)
1558 {
1559 yuv[x] = calloc(rows[x], dstStep[x]);
1560 ref[x] = calloc(rows[x], dstStep[x]);
1561 if (!yuv[x] || !ref[x])
1562 goto fail;
1563 }
1564 if (winpr_RAND(rgb, roi.height * srcStep) < 0)
1565 goto fail;
1566
1567 for (size_t f = 0; f < ARRAYSIZE(formats); f++)
1568 {
1569 const UINT32 format = formats[f];
1570 for (size_t x = 0; x < 3; x++)
1571 {
1572 memset(yuv[x], PADDING_FILL_VALUE, 1ULL * rows[x] * dstStep[x]);
1573 memset(ref[x], PADDING_FILL_VALUE, 1ULL * rows[x] * dstStep[x]);
1574 }
1575
1576 if (soft->RGBToYUV420_8u_P3AC4R(rgb, format, (UINT32)srcStep, ref, dstStep, &roi) !=
1577 PRIMITIVES_SUCCESS)
1578 goto fail;
1579 if (prims->RGBToYUV420_8u_P3AC4R(rgb, format, (UINT32)srcStep, yuv, dstStep, &roi) !=
1580 PRIMITIVES_SUCCESS)
1581 goto fail;
1582
1583 for (size_t x = 0; x < 3; x++)
1584 {
1585 for (size_t y = 0; y < rows[x]; y++)
1586 {
1587 const BYTE* line = &yuv[x][y * dstStep[x]];
1588 const BYTE* expect = &ref[x][y * dstStep[x]];
1589 if (!yuv_encode_close(line, expect, used[x]))
1590 {
1591 (void)fprintf(stderr,
1592 "[%s] %s %" PRIu32 "x%" PRIu32 ": plane %" PRIuz
1593 " differs in line %" PRIuz "\n",
1594 __func__, FreeRDPGetColorFormatName(format), roi.width,
1595 roi.height, x, y);
1596 goto fail;
1597 }
1598 for (size_t p = used[x]; p < dstStep[x]; p++)
1599 {
1600 if (line[p] != PADDING_FILL_VALUE)
1601 {
1602 (void)fprintf(stderr, "[%s] %s: plane %" PRIuz " overwrote padding\n",
1603 __func__, FreeRDPGetColorFormatName(format), x);
1604 goto fail;
1605 }
1606 }
1607 }
1608 }
1609 }
1610
1611 rc = TRUE;
1612fail:
1613 printf("%s %" PRIu32 "x%" PRIu32 " finished with %s\n", __func__, roi.width, roi.height,
1614 rc ? "SUCCESS" : "FAILURE");
1615 free(rgb);
1616 for (size_t x = 0; x < 3; x++)
1617 {
1618 free(yuv[x]);
1619 free(ref[x]);
1620 }
1621 return rc;
1622}
1623
1624/* RGBToAVC444YUV and RGBToAVC444YUVv2 against the generic routines at the same sizes, the
1625 * values within YUV_ENCODE_TOLERANCE. Both views are compared whole: the buffers start with
1626 * the same fill, so a byte written in the wrong place shows up as well. */
1627static BOOL compare_rgb_to_avc444_edges(prim_size_t roi, DWORD type, UINT32 version)
1628{
1629 BOOL rc = FALSE;
1630 const UINT32 formats[] = { PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBX32 };
1631 const size_t srcStep = 4ULL * roi.width + 12;
1632 const UINT32 step = roi.width + 24;
1633 /* the v1 auxiliary view writes up to 8 rows past the row pair */
1634 const size_t rows = roi.height + 24ULL;
1635 const UINT32 dstStep[3] = { step, step, step };
1636 BYTE* rgb = nullptr;
1637 BYTE* main1[3] = WINPR_C_ARRAY_INIT;
1638 BYTE* aux1[3] = WINPR_C_ARRAY_INIT;
1639 BYTE* main2[3] = WINPR_C_ARRAY_INIT;
1640 BYTE* aux2[3] = WINPR_C_ARRAY_INIT;
1641
1642 primitives_t* prims = primitives_get_by_type(type);
1643 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1644 if (!prims)
1645 {
1646 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1647 return TRUE;
1648 }
1649 if (!soft)
1650 goto fail;
1651
1652 const fn_RGBToAVC444YUV_t fkt =
1653 (version == 1) ? prims->RGBToAVC444YUV : prims->RGBToAVC444YUVv2;
1654 const fn_RGBToAVC444YUV_t gen = (version == 1) ? soft->RGBToAVC444YUV : soft->RGBToAVC444YUVv2;
1655 if (!fkt || !gen)
1656 goto fail;
1657
1658 rgb = calloc(roi.height, srcStep);
1659 if (!rgb)
1660 goto fail;
1661 for (size_t x = 0; x < 3; x++)
1662 {
1663 main1[x] = calloc(rows, step);
1664 aux1[x] = calloc(rows, step);
1665 main2[x] = calloc(rows, step);
1666 aux2[x] = calloc(rows, step);
1667 if (!main1[x] || !aux1[x] || !main2[x] || !aux2[x])
1668 goto fail;
1669 }
1670 if (winpr_RAND(rgb, roi.height * srcStep) < 0)
1671 goto fail;
1672
1673 for (size_t f = 0; f < ARRAYSIZE(formats); f++)
1674 {
1675 const UINT32 format = formats[f];
1676 for (size_t x = 0; x < 3; x++)
1677 {
1678 memset(main1[x], PADDING_FILL_VALUE, rows * step);
1679 memset(aux1[x], PADDING_FILL_VALUE, rows * step);
1680 memset(main2[x], PADDING_FILL_VALUE, rows * step);
1681 memset(aux2[x], PADDING_FILL_VALUE, rows * step);
1682 }
1683
1684 if (gen(rgb, format, (UINT32)srcStep, main1, dstStep, aux1, dstStep, &roi) !=
1685 PRIMITIVES_SUCCESS)
1686 goto fail;
1687 if (fkt(rgb, format, (UINT32)srcStep, main2, dstStep, aux2, dstStep, &roi) !=
1688 PRIMITIVES_SUCCESS)
1689 goto fail;
1690
1691 for (size_t x = 0; x < 3; x++)
1692 {
1693 const BOOL mainClose = yuv_encode_close(main1[x], main2[x], rows * step);
1694 if (!mainClose || !yuv_encode_close(aux1[x], aux2[x], rows * step))
1695 {
1696 (void)fprintf(stderr,
1697 "[%s] v%" PRIu32 " %s %" PRIu32 "x%" PRIu32 ": plane %" PRIuz
1698 " of the %s view differs\n",
1699 __func__, version, FreeRDPGetColorFormatName(format), roi.width,
1700 roi.height, x, mainClose ? "auxiliary" : "main");
1701 goto fail;
1702 }
1703 }
1704 }
1705
1706 rc = TRUE;
1707fail:
1708 printf("%s v%" PRIu32 " %" PRIu32 "x%" PRIu32 " finished with %s\n", __func__, version,
1709 roi.width, roi.height, rc ? "SUCCESS" : "FAILURE");
1710 free(rgb);
1711 for (size_t x = 0; x < 3; x++)
1712 {
1713 free(main1[x]);
1714 free(aux1[x]);
1715 free(main2[x]);
1716 free(aux2[x]);
1717 }
1718 return rc;
1719}
1720
1721static BOOL compare_rgb_to_yuv420(prim_size_t roi, DWORD type)
1722{
1723 BOOL rc = FALSE;
1724 const UINT32 format = PIXEL_FORMAT_BGRA32;
1725 const size_t stride = 4ULL * roi.width;
1726 const UINT32 yuvStep[] = { roi.width, roi.width / 2, roi.width / 2 };
1727 BYTE* yuv1[3] = WINPR_C_ARRAY_INIT;
1728 BYTE* yuv2[3] = WINPR_C_ARRAY_INIT;
1729
1730 primitives_t* prims = primitives_get_by_type(type);
1731 if (!prims)
1732 {
1733 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1734 return TRUE;
1735 }
1736
1737 BYTE* rgb = calloc(roi.height, stride);
1738 BYTE* rgbcopy = calloc(roi.height, stride);
1739
1740 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1741 if (!soft || !rgb || !rgbcopy)
1742 goto fail;
1743
1744 if (winpr_RAND(rgb, roi.height * stride) < 0)
1745 goto fail;
1746 memcpy(rgbcopy, rgb, roi.height * stride);
1747
1748 if (!allocate_yuv(yuv1, roi) || !allocate_yuv(yuv2, roi))
1749 goto fail;
1750
1751 if (soft->RGBToYUV420_8u_P3AC4R(rgb, format, stride, yuv1, yuvStep, &roi) != PRIMITIVES_SUCCESS)
1752 goto fail;
1753 if (memcmp(rgb, rgbcopy, roi.height * stride) != 0)
1754 goto fail;
1755 if (prims->RGBToYUV420_8u_P3AC4R(rgb, format, stride, yuv2, yuvStep, &roi) !=
1756 PRIMITIVES_SUCCESS)
1757 goto fail;
1758
1759 for (size_t y = 0; y < roi.height; y++)
1760 {
1761 // odd lines do produce artefacts in last line, skip check
1762 if (((y + 1) >= roi.height) && ((y % 2) == 0))
1763 continue;
1764
1765 const BYTE* yline1[3] = {
1766 &yuv1[0][y * yuvStep[0]],
1767 &yuv1[1][(y / 2) * yuvStep[1]],
1768 &yuv1[2][(y / 2) * yuvStep[2]],
1769 };
1770 const BYTE* yline2[3] = {
1771 &yuv2[0][y * yuvStep[0]],
1772 &yuv2[1][(y / 2) * yuvStep[1]],
1773 &yuv2[2][(y / 2) * yuvStep[2]],
1774 };
1775
1776 for (size_t x = 0; x < ARRAYSIZE(yline1); x++)
1777 {
1778 if (similarY(yline1[x], yline2[x], yuvStep[x], x) != 0)
1779 {
1780 (void)fprintf(stderr,
1781 "[%s] compare failed in component %" PRIuz ", line %" PRIuz "\n",
1782 __func__, x, y);
1783 (void)fprintf(stderr, "[%s] roi %" PRIu32 "x%" PRIu32 "\n", __func__, roi.width,
1784 roi.height);
1785 winpr_HexDump(TAG, WLOG_WARN, yline1[x], yuvStep[x]);
1786 winpr_HexDump(TAG, WLOG_WARN, yline2[x], yuvStep[x]);
1787 winpr_HexDump(TAG, WLOG_WARN, &rgb[y * stride], stride);
1788 goto fail;
1789 }
1790 }
1791 }
1792
1793 rc = TRUE;
1794fail:
1795 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1796 free(rgb);
1797 free(rgbcopy);
1798 free_yuv(yuv1);
1799 free_yuv(yuv2);
1800
1801 return rc;
1802}
1803
1804/* Check the result of generic matches the optimized routine for the AVC444
1805 * combine: a luma (main) view followed by a v1 or v2 chroma (auxiliary) view,
1806 * on random data. Constant input planes cannot tell a misplaced column from a
1807 * correct one. */
1808static BOOL compare_yuv420_combine_to_yuv444(prim_size_t roi, DWORD type, avc444_frame_type chroma)
1809{
1810 BOOL rc = FALSE;
1811 /* Even sizes only: for an odd height the generic and SSE v1 paths still
1812 * disagree on the last row (nHeight / 2 against (nHeight + 1) / 2). */
1813 roi.width = MAX(roi.width & ~1u, 2u);
1814 roi.height = MAX(roi.height & ~1u, 2u);
1815 const UINT32 awidth = (roi.width + 31) & ~31u;
1816 const UINT32 aheight = (roi.height + 15) & ~15u;
1817 const UINT32 srcStep[3] = { awidth, awidth / 2, awidth / 2 };
1818 const UINT32 dstStep[3] = { awidth, awidth, awidth };
1819 const RECTANGLE_16 rect = { 0, 0, WINPR_ASSERTING_INT_CAST(UINT16, roi.width),
1820 WINPR_ASSERTING_INT_CAST(UINT16, roi.height) };
1821 BYTE* luma[3] = WINPR_C_ARRAY_INIT;
1822 BYTE* aux[3] = WINPR_C_ARRAY_INIT;
1823 BYTE* dst1[3] = WINPR_C_ARRAY_INIT;
1824 BYTE* dst2[3] = WINPR_C_ARRAY_INIT;
1825
1826 primitives_t* prims = primitives_get_by_type(type);
1827 if (!prims)
1828 {
1829 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1830 return TRUE;
1831 }
1832
1833 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1834 if (!soft)
1835 goto fail;
1836
1837 for (size_t x = 0; x < 3; x++)
1838 {
1839 const size_t srcSize = 1ULL * srcStep[x] * ((x > 0) ? aheight / 2 : aheight);
1840 luma[x] = calloc(srcSize, 1);
1841 aux[x] = calloc(srcSize, 1);
1842 dst1[x] = calloc(dstStep[x], aheight);
1843 dst2[x] = calloc(dstStep[x], aheight);
1844 if (!luma[x] || !aux[x] || !dst1[x] || !dst2[x])
1845 goto fail;
1846 if (winpr_RAND(luma[x], srcSize) < 0)
1847 goto fail;
1848 if (winpr_RAND(aux[x], srcSize) < 0)
1849 goto fail;
1850 }
1851
1852 {
1853 const BYTE* cluma[] = { luma[0], luma[1], luma[2] };
1854 const BYTE* caux[] = { aux[0], aux[1], aux[2] };
1855
1856 if (soft->YUV420CombineToYUV444(AVC444_LUMA, cluma, srcStep, awidth, aheight, dst1, dstStep,
1857 &rect) != PRIMITIVES_SUCCESS)
1858 goto fail;
1859 if (soft->YUV420CombineToYUV444(chroma, caux, srcStep, awidth, aheight, dst1, dstStep,
1860 &rect) != PRIMITIVES_SUCCESS)
1861 goto fail;
1862 if (prims->YUV420CombineToYUV444(AVC444_LUMA, cluma, srcStep, awidth, aheight, dst2,
1863 dstStep, &rect) != PRIMITIVES_SUCCESS)
1864 goto fail;
1865 if (prims->YUV420CombineToYUV444(chroma, caux, srcStep, awidth, aheight, dst2, dstStep,
1866 &rect) != PRIMITIVES_SUCCESS)
1867 goto fail;
1868 }
1869
1870 for (size_t x = 0; x < 3; x++)
1871 {
1872 for (size_t y = 0; y < roi.height; y++)
1873 {
1874 const BYTE* line1 = &dst1[x][y * dstStep[x]];
1875 const BYTE* line2 = &dst2[x][y * dstStep[x]];
1876
1877 for (size_t z = 0; z < roi.width; z++)
1878 {
1879 if (line1[z] != line2[z])
1880 {
1881 (void)fprintf(
1882 stderr,
1883 "[%s] plane %" PRIuz " [%" PRIuz "x%" PRIuz
1884 "] generic and optimized data mismatch: 0x%02" PRIx8 " != 0x%02" PRIx8 "\n",
1885 (chroma == AVC444_CHROMAv1) ? "v1" : "v2", x, z, y, line1[z], line2[z]);
1886 (void)fprintf(stderr, "roi: %" PRIu32 "x%" PRIu32 "\n", roi.width, roi.height);
1887 goto fail;
1888 }
1889 }
1890 }
1891 }
1892
1893 rc = TRUE;
1894fail:
1895 printf("%s [%s] finished with %s\n", __func__, (chroma == AVC444_CHROMAv1) ? "v1" : "v2",
1896 rc ? "SUCCESS" : "FAILURE");
1897 for (size_t x = 0; x < 3; x++)
1898 {
1899 free(luma[x]);
1900 free(aux[x]);
1901 free(dst1[x]);
1902 free(dst2[x]);
1903 }
1904 return rc;
1905}
1906
1907int TestPrimitivesYUV(int argc, char* argv[])
1908{
1909 BOOL large = (argc > 1);
1910 int rc = -1;
1911 WINPR_UNUSED(argc);
1912 WINPR_UNUSED(argv);
1913 prim_size_t roi = WINPR_C_ARRAY_INIT;
1914
1915 if (argc > 1)
1916 {
1917 BOOL reset = TRUE;
1918 char* str = argv[1];
1919 char* ptr = strchr(str, 'x');
1920 if (ptr)
1921 {
1922 *ptr++ = '\0';
1923
1924 errno = 0;
1925 roi.width = strtoul(str, nullptr, 0);
1926 if (errno == 0)
1927 roi.height = strtoul(str, nullptr, 0);
1928 reset = errno != 0;
1929 }
1930
1931 if (reset)
1932 {
1933 roi.width = 1920;
1934 roi.height = 1080;
1935 }
1936 }
1937 else
1938 {
1939 if (!get_size(large, &roi.width, &roi.height))
1940 goto end;
1941 }
1942
1943 prim_test_setup(FALSE);
1944
1945 for (UINT32 type = PRIMITIVES_PURE_SOFT; type <= PRIMITIVES_AUTODETECT; type++)
1946 {
1947 if (!compare_yuv444_to_rgb(roi, type))
1948 goto end;
1949
1950 const prim_size_t oddroi = { roi.width - 1, roi.height };
1951 if (!compare_i444_to_rgb(roi, type) || !compare_i444_to_rgb(oddroi, type))
1952 goto end;
1953
1954 if (!compare_rgb_to_yuv444(roi, type))
1955 goto end;
1956
1957 /* the random roi plus widths around the 16 (SSE4.1, NEON) and 32 (AVX2)
1958 * pixel vector loops */
1959 const prim_size_t i444Rois[] = { roi, { 1, 1 }, { 15, 3 }, { 16, 2 }, { 17, 2 },
1960 { 31, 2 }, { 32, 2 }, { 33, 2 }, { 47, 5 }, { 63, 3 } };
1961 for (size_t x = 0; x < ARRAYSIZE(i444Rois); x++)
1962 {
1963 if (!compare_rgb_to_i444(i444Rois[x], type))
1964 goto end;
1965 }
1966
1967 if (!compare_yuv420_to_rgb(roi, type))
1968 goto end;
1969 if (!compare_rgb_to_yuv420(roi, type))
1970 goto end;
1971 /* the random roi plus sizes around the 16x2 vector blocks */
1972 const prim_size_t yuv420Rois[] = {
1973 roi, { 1, 1 }, { 15, 3 }, { 16, 2 }, { 17, 3 },
1974 { 32, 1 }, { 33, 5 }, { 47, 4 }, { 48, 40 }, { 64, 17 }
1975 };
1976 for (size_t x = 0; x < ARRAYSIZE(yuv420Rois); x++)
1977 {
1978 if (!compare_rgb_to_yuv420_edges(yuv420Rois[x], type))
1979 goto end;
1980 if (!compare_rgb_to_avc444_edges(yuv420Rois[x], type, 1) ||
1981 !compare_rgb_to_avc444_edges(yuv420Rois[x], type, 2))
1982 goto end;
1983 }
1984
1985 /* the random roi may be too narrow for the vector loops; 1744 is an
1986 * odd number of macroblocks */
1987 const prim_size_t combineRois[] = { roi, { 1744, 64 } };
1988 for (size_t x = 0; x < ARRAYSIZE(combineRois); x++)
1989 {
1990 if (!compare_yuv420_combine_to_yuv444(combineRois[x], type, AVC444_CHROMAv1))
1991 goto end;
1992 if (!compare_yuv420_combine_to_yuv444(combineRois[x], type, AVC444_CHROMAv2))
1993 goto end;
1994 }
1995 }
1996
1997 if (!run_tests(roi))
1998 goto end;
1999
2000 rc = 0;
2001end:
2002 printf("[%s] finished, status %s [%d]\n", __func__, (rc == 0) ? "SUCCESS" : "FAILURE", rc);
2003 return rc;
2004}
WINPR_ATTR_NODISCARD fn_RGBToYUV444_8u_P3AC4R_t RGBToI444_8u
Definition primitives.h:305
WINPR_ATTR_NODISCARD fn_YUV444ToRGB_8u_P3AC4R_t I444ToRGB_8u
Convert a plain YUV444 picture (e.g. decoded AV1) to RGB. Unlike YUV444ToRGB_8u_P3AC4R this does not ...
Definition primitives.h:310