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. */
25static BOOL similar(const BYTE* src, const BYTE* dst, size_t size)
26{
27 for (size_t x = 0; x < size; x++)
28 {
29 int diff = src[x] - dst[x];
30
31 if (abs(diff) > 4)
32 {
33 (void)fprintf(stderr, "%" PRIuz " %02" PRIX8 " : %02" PRIX8 " diff=%d\n", x, src[x],
34 dst[x], abs(diff));
35 return FALSE;
36 }
37 }
38
39 return TRUE;
40}
41
42static BOOL similarRGB(size_t y, const BYTE* src, const BYTE* dst, size_t size, UINT32 format,
43 BOOL use444)
44{
45 BOOL rc = TRUE;
46 /* Skip YUV420, too lossy */
47 if (!use444)
48 return TRUE;
49
50 const UINT32 bpp = FreeRDPGetBytesPerPixel(format);
51 BYTE fill = PADDING_FILL_VALUE;
52 if (!FreeRDPColorHasAlpha(format))
53 fill = 0xFF;
54
55 for (size_t x = 0; x < size; x++)
56 {
57 const LONG maxDiff = 4;
58 UINT32 sColor = 0;
59 UINT32 dColor = 0;
60 BYTE sR = 0;
61 BYTE sG = 0;
62 BYTE sB = 0;
63 BYTE sA = 0;
64 BYTE dR = 0;
65 BYTE dG = 0;
66 BYTE dB = 0;
67 BYTE dA = 0;
68 sColor = FreeRDPReadColor(src, format);
69 dColor = FreeRDPReadColor(dst, format);
70 src += bpp;
71 dst += bpp;
72 FreeRDPSplitColor(sColor, format, &sR, &sG, &sB, &sA, nullptr);
73 FreeRDPSplitColor(dColor, format, &dR, &dG, &dB, &dA, nullptr);
74
75 const long diffr = labs(1L * sR - dR);
76 const long diffg = labs(1L * sG - dG);
77 const long diffb = labs(1L * sB - dB);
78 if ((diffr > maxDiff) || (diffg > maxDiff) || (diffb > maxDiff))
79 {
80 /* AVC444 uses an averaging filter for luma pixel U/V and reverses it in YUV444 -> RGB
81 * this is lossy and does not handle all combinations well so the 2x,2y pixel can be
82 * quite different after RGB -> YUV444 -> RGB conversion.
83 *
84 * skip these pixels to avoid failing the test
85 */
86 if (use444 && ((x % 2) == 0) && ((y % 2) == 0))
87 {
88 continue;
89 }
90
91 const BYTE sY = RGB2Y(sR, sG, sB);
92 const BYTE sU = RGB2U(sR, sG, sB);
93 const BYTE sV = RGB2V(sR, sG, sB);
94 const BYTE dY = RGB2Y(dR, dG, dB);
95 const BYTE dU = RGB2U(dR, dG, dB);
96 const BYTE dV = RGB2V(dR, dG, dB);
97 (void)fprintf(stderr,
98 "[%s] Color value mismatch R[%02X %02X], G[%02X %02X], B[%02X %02X] at "
99 "position %" PRIuz "x%" PRIuz "\n",
100 use444 ? "AVC444" : "AVC420", sR, dR, sG, dG, sA, dA, x, y);
101 (void)fprintf(stderr,
102 "[%s] Color value mismatch Y[%02X %02X], U[%02X %02X], V[%02X %02X] at "
103 "position %" PRIuz "x%" PRIuz "\n",
104 use444 ? "AVC444" : "AVC420", sY, dY, sU, dU, sV, dV, x, y);
105 rc = FALSE;
106 continue;
107 }
108
109 if (dA != fill)
110 {
111 (void)fprintf(
112 stderr,
113 "[%s] Invalid destination alpha value 0x%02X [expected 0x%02X] at position %" PRIuz
114 "x%" PRIuz "\n",
115 use444 ? "AVC444" : "AVC420", dA, fill, x, y);
116 rc = FALSE;
117 continue;
118 }
119 }
120
121 return rc;
122}
123
124static BOOL get_size(BOOL large, UINT32* width, UINT32* height)
125{
126 UINT32 shift = large ? 8 : 1;
127 if (winpr_RAND(width, sizeof(*width)) < 0)
128 return FALSE;
129 if (winpr_RAND(height, sizeof(*height)) < 0)
130 return FALSE;
131 *width = (*width % 64 + 1) << shift;
132 *height = (*height % 64 + 1);
133 return TRUE;
134}
135
136static BOOL check_padding(const BYTE* psrc, size_t size, size_t padding, const char* buffer)
137{
138 BOOL rc = TRUE;
139 const BYTE* src = nullptr;
140 const BYTE* esrc = nullptr;
141 size_t halfPad = (padding + 1) / 2;
142
143 if (!psrc)
144 return FALSE;
145
146 src = psrc - halfPad;
147 esrc = src + size + halfPad;
148
149 for (size_t x = 0; x < halfPad; x++)
150 {
151 const BYTE s = *src++;
152 const BYTE d = *esrc++;
153
154 if (s != 'A')
155 {
156 size_t start = x;
157
158 while ((x < halfPad) && (*esrc++ != 'A'))
159 x++;
160
161 (void)fprintf(stderr,
162 "Buffer underflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
163 "]\n",
164 d, 'A', buffer, start, x);
165 return FALSE;
166 }
167
168 if (d != 'A')
169 {
170 size_t start = x;
171
172 while ((x < halfPad) && (*esrc++ != 'A'))
173 x++;
174
175 (void)fprintf(stderr,
176 "Buffer overflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
177 "]\n",
178 d, 'A', buffer, start, x);
179 return FALSE;
180 }
181 }
182
183 return rc;
184}
185
186static void* set_padding(size_t size, size_t padding)
187{
188 size_t halfPad = (padding + 1) / 2;
189 BYTE* psrc = nullptr;
190 BYTE* src = winpr_aligned_malloc(size + 2 * halfPad, 16);
191
192 if (!src)
193 return nullptr;
194
195 memset(&src[0], 'A', halfPad);
196 memset(&src[halfPad], PADDING_FILL_VALUE, size);
197 memset(&src[halfPad + size], 'A', halfPad);
198 psrc = &src[halfPad];
199
200 if (!check_padding(psrc, size, padding, "init"))
201 {
202 winpr_aligned_free(src);
203 return nullptr;
204 }
205
206 return psrc;
207}
208
209static void free_padding(void* src, size_t padding)
210{
211 BYTE* ptr = nullptr;
212
213 if (!src)
214 return;
215
216 ptr = ((BYTE*)src) - (padding + 1) / 2;
217 winpr_aligned_free(ptr);
218}
219
220/* Create 2 pseudo YUV420 frames of same size.
221 * Combine them and check, if the data is at the expected position. */
222static BOOL TestPrimitiveYUVCombine(primitives_t* prims, prim_size_t roi)
223{
224 union
225 {
226 const BYTE** cpv;
227 BYTE** pv;
228 } cnv;
229 size_t awidth = 0;
230 size_t aheight = 0;
231 BOOL rc = FALSE;
232 BYTE* luma[3] = WINPR_C_ARRAY_INIT;
233 BYTE* chroma[3] = WINPR_C_ARRAY_INIT;
234 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
235 BYTE* pmain[3] = WINPR_C_ARRAY_INIT;
236 BYTE* paux[3] = WINPR_C_ARRAY_INIT;
237 UINT32 lumaStride[3] = WINPR_C_ARRAY_INIT;
238 UINT32 chromaStride[3] = WINPR_C_ARRAY_INIT;
239 UINT32 yuvStride[3] = WINPR_C_ARRAY_INIT;
240 const size_t padding = 10000;
241 RECTANGLE_16 rect = WINPR_C_ARRAY_INIT;
242 PROFILER_DEFINE(yuvCombine)
243 PROFILER_DEFINE(yuvSplit)
244
245 // TODO: we only support even height values at the moment
246 if (roi.height % 2)
247 roi.height++;
248
249 awidth = roi.width + 16 - roi.width % 16;
250 aheight = roi.height + 16 - roi.height % 16;
251 (void)fprintf(stderr,
252 "Running YUVCombine on frame size %" PRIu32 "x%" PRIu32 " [%" PRIuz "x%" PRIuz
253 "]\n",
254 roi.width, roi.height, awidth, aheight);
255 PROFILER_CREATE(yuvCombine, "YUV420CombineToYUV444")
256 PROFILER_CREATE(yuvSplit, "YUV444SplitToYUV420")
257 rect.left = 0;
258 rect.top = 0;
259 rect.right = roi.width;
260 rect.bottom = roi.height;
261
262 if (!prims || !prims->YUV420CombineToYUV444)
263 goto fail;
264
265 for (UINT32 x = 0; x < 3; x++)
266 {
267 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
268 size_t size = aheight * awidth;
269 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
270 yuvStride[x] = awidth;
271
272 if (!(yuv[x] = set_padding(size, padding)))
273 goto fail;
274
275 lumaStride[x] = halfStride;
276
277 if (!(luma[x] = set_padding(halfSize, padding)))
278 goto fail;
279
280 if (!(pmain[x] = set_padding(halfSize, padding)))
281 goto fail;
282
283 chromaStride[x] = halfStride;
284
285 if (!(chroma[x] = set_padding(halfSize, padding)))
286 goto fail;
287
288 if (!(paux[x] = set_padding(halfSize, padding)))
289 goto fail;
290
291 memset(luma[x], WINPR_ASSERTING_INT_CAST(int, 0xAB + 3 * x), halfSize);
292 memset(chroma[x], WINPR_ASSERTING_INT_CAST(int, 0x80 + 2 * x), halfSize);
293
294 if (!check_padding(luma[x], halfSize, padding, "luma"))
295 goto fail;
296
297 if (!check_padding(chroma[x], halfSize, padding, "chroma"))
298 goto fail;
299
300 if (!check_padding(pmain[x], halfSize, padding, "main"))
301 goto fail;
302
303 if (!check_padding(paux[x], halfSize, padding, "aux"))
304 goto fail;
305
306 if (!check_padding(yuv[x], size, padding, "yuv"))
307 goto fail;
308 }
309
310 PROFILER_ENTER(yuvCombine)
311
312 cnv.pv = luma;
313 if (prims->YUV420CombineToYUV444(AVC444_LUMA, cnv.cpv, lumaStride, roi.width, roi.height, yuv,
314 yuvStride, &rect) != PRIMITIVES_SUCCESS)
315 {
316 PROFILER_EXIT(yuvCombine)
317 goto fail;
318 }
319
320 cnv.pv = chroma;
321 if (prims->YUV420CombineToYUV444(AVC444_CHROMAv1, cnv.cpv, chromaStride, roi.width, roi.height,
322 yuv, yuvStride, &rect) != PRIMITIVES_SUCCESS)
323 {
324 PROFILER_EXIT(yuvCombine)
325 goto fail;
326 }
327
328 PROFILER_EXIT(yuvCombine)
329
330 for (size_t x = 0; x < 3; x++)
331 {
332 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
333 size_t size = 1ULL * aheight * awidth;
334 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
335
336 if (!check_padding(luma[x], halfSize, padding, "luma"))
337 goto fail;
338
339 if (!check_padding(chroma[x], halfSize, padding, "chroma"))
340 goto fail;
341
342 if (!check_padding(yuv[x], size, padding, "yuv"))
343 goto fail;
344 }
345
346 PROFILER_ENTER(yuvSplit)
347
348 cnv.pv = yuv;
349 if (prims->YUV444SplitToYUV420(cnv.cpv, yuvStride, pmain, lumaStride, paux, chromaStride,
350 &roi) != PRIMITIVES_SUCCESS)
351 {
352 PROFILER_EXIT(yuvSplit)
353 goto fail;
354 }
355
356 PROFILER_EXIT(yuvSplit)
357
358 for (UINT32 x = 0; x < 3; x++)
359 {
360 size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
361 size_t size = aheight * awidth;
362 size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
363
364 if (!check_padding(pmain[x], halfSize, padding, "main"))
365 goto fail;
366
367 if (!check_padding(paux[x], halfSize, padding, "aux"))
368 goto fail;
369
370 if (!check_padding(yuv[x], size, padding, "yuv"))
371 goto fail;
372 }
373
374 for (size_t i = 0; i < 3; i++)
375 {
376 for (size_t y = 0; y < roi.height; y++)
377 {
378 UINT32 w = roi.width;
379 UINT32 lstride = lumaStride[i];
380 UINT32 cstride = chromaStride[i];
381
382 if (i > 0)
383 {
384 w = (roi.width + 3) / 4;
385
386 if (roi.height > (roi.height + 1) / 2)
387 continue;
388 }
389
390 if (!similar(luma[i] + y * lstride, pmain[i] + y * lstride, w))
391 goto fail;
392
393 /* Need to ignore lines of destination Y plane,
394 * if the lines are not a multiple of 16
395 * as the UV planes are packed in 8 line stripes. */
396 if (i == 0)
397 {
398 /* TODO: This check is not perfect, it does not
399 * include the last V lines packed to the Y
400 * frame. */
401 UINT32 rem = roi.height % 16;
402
403 if (y > roi.height - rem)
404 continue;
405 }
406
407 if (!similar(chroma[i] + y * cstride, paux[i] + y * cstride, w))
408 goto fail;
409 }
410 }
411
412 PROFILER_PRINT_HEADER
413 PROFILER_PRINT(yuvSplit)
414 PROFILER_PRINT(yuvCombine)
415 PROFILER_PRINT_FOOTER
416 rc = TRUE;
417fail:
418 printf("[%s] run %s.\n", __func__, (rc) ? "SUCCESS" : "FAILED");
419 PROFILER_FREE(yuvCombine)
420 PROFILER_FREE(yuvSplit)
421
422 for (UINT32 x = 0; x < 3; x++)
423 {
424 free_padding(yuv[x], padding);
425 free_padding(luma[x], padding);
426 free_padding(chroma[x], padding);
427 free_padding(pmain[x], padding);
428 free_padding(paux[x], padding);
429 }
430
431 return rc;
432}
433
434static BOOL TestPrimitiveYUV(primitives_t* prims, prim_size_t roi, BOOL use444)
435{
436 union
437 {
438 const BYTE** cpv;
439 BYTE** pv;
440 } cnv;
441 BOOL res = FALSE;
442 UINT32 awidth = 0;
443 UINT32 aheight = 0;
444 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
445 UINT32 yuv_step[3] = WINPR_C_ARRAY_INIT;
446 BYTE* rgb = nullptr;
447 BYTE* rgb_dst = nullptr;
448 size_t size = 0;
449 size_t uvsize = 0;
450 size_t uvwidth = 0;
451 size_t padding = 100ULL * 16ULL;
452 UINT32 stride = 0;
453 const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
454 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
455 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
456 PROFILER_DEFINE(rgbToYUV420)
457 PROFILER_DEFINE(rgbToYUV444)
458 PROFILER_DEFINE(yuv420ToRGB)
459 PROFILER_DEFINE(yuv444ToRGB)
460 /* Buffers need to be 16x16 aligned. */
461 awidth = roi.width + 16 - roi.width % 16;
462 aheight = roi.height + 16 - roi.height % 16;
463 stride = 1ULL * awidth * sizeof(UINT32);
464 size = 1ULL * awidth * aheight;
465
466 if (use444)
467 {
468 uvwidth = awidth;
469 uvsize = size;
470
471 if (!prims || !prims->RGBToYUV444_8u_P3AC4R || !prims->YUV444ToRGB_8u_P3AC4R)
472 return FALSE;
473 }
474 else
475 {
476 uvwidth = (awidth + 1) / 2;
477 uvsize = (aheight + 1) / 2 * uvwidth;
478
479 if (!prims || !prims->RGBToYUV420_8u_P3AC4R || !prims->YUV420ToRGB_8u_P3AC4R)
480 return FALSE;
481 }
482
483 (void)fprintf(stderr, "Running AVC%s on frame size %" PRIu32 "x%" PRIu32 "\n",
484 use444 ? "444" : "420", roi.width, roi.height);
485
486 /* Test RGB to YUV444 conversion and vice versa */
487 if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
488 goto fail;
489
490 if (!(rgb_dst = set_padding(size * sizeof(UINT32), padding)))
491 goto fail;
492
493 if (!(yuv[0] = set_padding(size, padding)))
494 goto fail;
495
496 if (!(yuv[1] = set_padding(uvsize, padding)))
497 goto fail;
498
499 if (!(yuv[2] = set_padding(uvsize, padding)))
500 goto fail;
501
502 for (size_t y = 0; y < roi.height; y++)
503 {
504 BYTE* line = &rgb[y * stride];
505 if (winpr_RAND(line, stride) < 0)
506 goto fail;
507 }
508
509 yuv_step[0] = awidth;
510 yuv_step[1] = uvwidth;
511 yuv_step[2] = uvwidth;
512
513 for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
514 {
515 pstatus_t rc = 0;
516 const UINT32 DstFormat = formats[x];
517 printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
518 memset(rgb_dst, PADDING_FILL_VALUE, size * sizeof(UINT32));
519
520 PROFILER_CREATE(rgbToYUV420, "RGBToYUV420")
521 PROFILER_CREATE(rgbToYUV444, "RGBToYUV444")
522 PROFILER_CREATE(yuv420ToRGB, "YUV420ToRGB")
523 PROFILER_CREATE(yuv444ToRGB, "YUV444ToRGB")
524
525 if (use444)
526 {
527 PROFILER_ENTER(rgbToYUV444)
528 rc = prims->RGBToYUV444_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
529 PROFILER_EXIT(rgbToYUV444)
530
531 if (rc != PRIMITIVES_SUCCESS)
532 goto loop_fail;
533
534 PROFILER_PRINT_HEADER
535 PROFILER_PRINT(rgbToYUV444)
536 PROFILER_PRINT_FOOTER
537 }
538 else
539 {
540 PROFILER_ENTER(rgbToYUV420)
541 rc = prims->RGBToYUV420_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
542 PROFILER_EXIT(rgbToYUV420)
543
544 if (rc != PRIMITIVES_SUCCESS)
545 goto loop_fail;
546
547 PROFILER_PRINT_HEADER
548 PROFILER_PRINT(rgbToYUV420)
549 PROFILER_PRINT_FOOTER
550 }
551
552 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
553 {
554 rc = -1;
555 goto loop_fail;
556 }
557
558 if ((!check_padding(yuv[0], size, padding, "Y")) ||
559 (!check_padding(yuv[1], uvsize, padding, "U")) ||
560 (!check_padding(yuv[2], uvsize, padding, "V")))
561 {
562 rc = -1;
563 goto loop_fail;
564 }
565
566 cnv.pv = yuv;
567 if (use444)
568 {
569 PROFILER_ENTER(yuv444ToRGB)
570 rc = prims->YUV444ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi);
571 PROFILER_EXIT(yuv444ToRGB)
572
573 if (rc != PRIMITIVES_SUCCESS)
574 goto loop_fail;
575
576 loop_fail:
577 PROFILER_EXIT(yuv444ToRGB)
578 PROFILER_PRINT_HEADER
579 PROFILER_PRINT(yuv444ToRGB)
580 PROFILER_PRINT_FOOTER
581
582 if (rc != PRIMITIVES_SUCCESS)
583 goto fail;
584 }
585 else
586 {
587 PROFILER_ENTER(yuv420ToRGB)
588
589 if (prims->YUV420ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi) !=
590 PRIMITIVES_SUCCESS)
591 {
592 PROFILER_EXIT(yuv420ToRGB)
593 goto fail;
594 }
595
596 PROFILER_EXIT(yuv420ToRGB)
597 PROFILER_PRINT_HEADER
598 PROFILER_PRINT(yuv420ToRGB)
599 PROFILER_PRINT_FOOTER
600 }
601
602 if (!check_padding(rgb_dst, size * sizeof(UINT32), padding, "rgb dst"))
603 goto fail;
604
605 if ((!check_padding(yuv[0], size, padding, "Y")) ||
606 (!check_padding(yuv[1], uvsize, padding, "U")) ||
607 (!check_padding(yuv[2], uvsize, padding, "V")))
608 goto fail;
609
610 BOOL equal = TRUE;
611 for (size_t y = 0; y < roi.height; y++)
612 {
613 BYTE* srgb = &rgb[y * stride];
614 BYTE* drgb = &rgb_dst[y * stride];
615
616 if (!similarRGB(y, srgb, drgb, roi.width, DstFormat, use444))
617 equal = FALSE;
618 }
619 if (!equal)
620 goto fail;
621
622 PROFILER_FREE(rgbToYUV420)
623 PROFILER_FREE(rgbToYUV444)
624 PROFILER_FREE(yuv420ToRGB)
625 PROFILER_FREE(yuv444ToRGB)
626 }
627
628 res = TRUE;
629fail:
630 printf("[%s] run %s.\n", __func__, (res) ? "SUCCESS" : "FAILED");
631 free_padding(rgb, padding);
632 free_padding(rgb_dst, padding);
633 free_padding(yuv[0], padding);
634 free_padding(yuv[1], padding);
635 free_padding(yuv[2], padding);
636 return res;
637}
638
639static BOOL allocate_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
640{
641 const size_t size = 1ULL * width * height;
642 const size_t uvwidth = (1ULL + width) / 2;
643 const size_t uvsize = (1ULL + height) / 2 * uvwidth;
644
645 if (!(planes[0] = set_padding(size, padding)))
646 goto fail;
647
648 if (!(planes[1] = set_padding(uvsize, padding)))
649 goto fail;
650
651 if (!(planes[2] = set_padding(uvsize, padding)))
652 goto fail;
653
654 return TRUE;
655fail:
656 free_padding(planes[0], padding);
657 free_padding(planes[1], padding);
658 free_padding(planes[2], padding);
659 return FALSE;
660}
661
662static void free_yuv420(BYTE** planes, UINT32 padding)
663{
664 if (!planes)
665 return;
666
667 free_padding(planes[0], padding);
668 free_padding(planes[1], padding);
669 free_padding(planes[2], padding);
670 planes[0] = nullptr;
671 planes[1] = nullptr;
672 planes[2] = nullptr;
673}
674
675static BOOL check_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
676{
677 const size_t size = 1ULL * width * height;
678 const size_t uvwidth = (width + 1) / 2;
679 const size_t uvsize = (height + 1) / 2 * uvwidth;
680 const BOOL yOk = check_padding(planes[0], size, padding, "Y");
681 const BOOL uOk = check_padding(planes[1], uvsize, padding, "U");
682 const BOOL vOk = check_padding(planes[2], uvsize, padding, "V");
683 return (yOk && uOk && vOk);
684}
685
686static BOOL check_for_mismatches(const BYTE* planeA, const BYTE* planeB, UINT32 size)
687{
688 BOOL rc = FALSE;
689
690 for (UINT32 x = 0; x < size; x++)
691 {
692 const BYTE a = planeA[x];
693 const BYTE b = planeB[x];
694
695 if (fabsf((float)a - (float)b) > 2.0f)
696 {
697 rc = TRUE;
698 (void)fprintf(stderr, "[%08x] %02x != %02x\n", x, a, b);
699 }
700 }
701
702 return rc;
703}
704
705static BOOL compare_yuv420(BYTE** planesA, BYTE** planesB, UINT32 width, UINT32 height,
706 UINT32 padding)
707{
708 BOOL rc = TRUE;
709 const size_t size = 1ULL * width * height;
710 const size_t uvwidth = (1ULL * width + 1) / 2;
711 const size_t uvsize = (1ULL * height + 1) / 2 * uvwidth;
712
713 if (check_for_mismatches(planesA[0], planesB[0], size))
714 {
715 (void)fprintf(stderr, "Mismatch in Y planes!\n");
716 rc = FALSE;
717 }
718
719 if (check_for_mismatches(planesA[1], planesB[1], uvsize))
720 {
721 (void)fprintf(stderr, "Mismatch in U planes!\n");
722 rc = FALSE;
723 }
724
725 if (check_for_mismatches(planesA[2], planesB[2], uvsize))
726 {
727 (void)fprintf(stderr, "Mismatch in V planes!\n");
728 rc = FALSE;
729 }
730
731 return rc;
732}
733
734static UINT32 prand(UINT32 max)
735{
736 UINT32 tmp = 0;
737 if (max <= 1)
738 return 1;
739 if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
740 {
741 (void)fprintf(stderr, "winpr_RAND failed, retry...\n");
742 // NOLINTNEXTLINE(concurrency-mt-unsafe)
743 exit(-1);
744 }
745 return tmp % (max - 1) + 1;
746}
747
748static BOOL TestPrimitiveRgbToLumaChroma(primitives_t* prims, prim_size_t roi, UINT32 version)
749{
750 BOOL res = FALSE;
751 UINT32 awidth = 0;
752 UINT32 aheight = 0;
753 BYTE* luma[3] = WINPR_C_ARRAY_INIT;
754 BYTE* chroma[3] = WINPR_C_ARRAY_INIT;
755 BYTE* lumaGeneric[3] = WINPR_C_ARRAY_INIT;
756 BYTE* chromaGeneric[3] = WINPR_C_ARRAY_INIT;
757 UINT32 yuv_step[3];
758 BYTE* rgb = nullptr;
759 size_t size = 0;
760 size_t uvwidth = 0;
761 const size_t padding = 0x1000;
762 UINT32 stride = 0;
763 fn_RGBToAVC444YUV_t fkt = nullptr;
764 fn_RGBToAVC444YUV_t gen = nullptr;
765 const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
766 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
767 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
768 PROFILER_DEFINE(rgbToYUV444)
769 PROFILER_DEFINE(rgbToYUV444opt)
770 /* Buffers need to be 16x16 aligned. */
771 awidth = roi.width;
772
773 if (awidth % 16 != 0)
774 awidth += 16 - roi.width % 16;
775
776 aheight = roi.height;
777
778 if (aheight % 16 != 0)
779 aheight += 16 - roi.height % 16;
780
781 stride = 1ULL * awidth * sizeof(UINT32);
782 size = 1ULL * awidth * aheight;
783 uvwidth = 1ULL * (awidth + 1) / 2;
784
785 if (!prims || !generic)
786 return FALSE;
787
788 switch (version)
789 {
790 case 1:
791 fkt = prims->RGBToAVC444YUV;
792 gen = generic->RGBToAVC444YUV;
793 break;
794
795 case 2:
796 fkt = prims->RGBToAVC444YUVv2;
797 gen = generic->RGBToAVC444YUVv2;
798 break;
799
800 default:
801 return FALSE;
802 }
803
804 if (!fkt || !gen)
805 return FALSE;
806
807 (void)fprintf(stderr, "Running AVC444 on frame size %" PRIu32 "x%" PRIu32 "\n", roi.width,
808 roi.height);
809
810 /* Test RGB to YUV444 conversion and vice versa */
811 if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
812 goto fail;
813
814 if (!allocate_yuv420(luma, awidth, aheight, padding))
815 goto fail;
816
817 if (!allocate_yuv420(chroma, awidth, aheight, padding))
818 goto fail;
819
820 if (!allocate_yuv420(lumaGeneric, awidth, aheight, padding))
821 goto fail;
822
823 if (!allocate_yuv420(chromaGeneric, awidth, aheight, padding))
824 goto fail;
825
826 for (size_t y = 0; y < roi.height; y++)
827 {
828 BYTE* line = &rgb[y * stride];
829
830 if (winpr_RAND(line, 4ULL * roi.width) < 0)
831 goto fail;
832 }
833
834 yuv_step[0] = awidth;
835 yuv_step[1] = uvwidth;
836 yuv_step[2] = uvwidth;
837
838 for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
839 {
840 pstatus_t rc = -1;
841 const UINT32 DstFormat = formats[x];
842 printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
843 PROFILER_CREATE(rgbToYUV444, "RGBToYUV444-generic")
844 PROFILER_CREATE(rgbToYUV444opt, "RGBToYUV444-optimized")
845
846 for (UINT32 cnt = 0; cnt < 10; cnt++)
847 {
848 PROFILER_ENTER(rgbToYUV444opt)
849 rc = fkt(rgb, DstFormat, stride, luma, yuv_step, chroma, yuv_step, &roi);
850 PROFILER_EXIT(rgbToYUV444opt)
851
852 if (rc != PRIMITIVES_SUCCESS)
853 goto loop_fail;
854 }
855
856 PROFILER_PRINT_HEADER
857 PROFILER_PRINT(rgbToYUV444opt)
858 PROFILER_PRINT_FOOTER
859
860 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
861 {
862 rc = -1;
863 goto loop_fail;
864 }
865
866 if (!check_yuv420(luma, awidth, aheight, padding) ||
867 !check_yuv420(chroma, awidth, aheight, padding))
868 {
869 rc = -1;
870 goto loop_fail;
871 }
872
873 for (UINT32 cnt = 0; cnt < 10; cnt++)
874 {
875 PROFILER_ENTER(rgbToYUV444)
876 rc = gen(rgb, DstFormat, stride, lumaGeneric, yuv_step, chromaGeneric, yuv_step, &roi);
877 PROFILER_EXIT(rgbToYUV444)
878
879 if (rc != PRIMITIVES_SUCCESS)
880 goto loop_fail;
881 }
882
883 PROFILER_PRINT_HEADER
884 PROFILER_PRINT(rgbToYUV444)
885 PROFILER_PRINT_FOOTER
886
887 if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
888 {
889 rc = -1;
890 goto loop_fail;
891 }
892
893 if (!check_yuv420(lumaGeneric, awidth, aheight, padding) ||
894 !check_yuv420(chromaGeneric, awidth, aheight, padding))
895 {
896 rc = -1;
897 goto loop_fail;
898 }
899
900 if (!compare_yuv420(luma, lumaGeneric, awidth, aheight, padding) ||
901 !compare_yuv420(chroma, chromaGeneric, awidth, aheight, padding))
902 {
903 rc = -1;
904 goto loop_fail;
905 }
906
907 loop_fail:
908 PROFILER_FREE(rgbToYUV444)
909 PROFILER_FREE(rgbToYUV444opt)
910
911 if (rc != PRIMITIVES_SUCCESS)
912 goto fail;
913 }
914
915 res = TRUE;
916fail:
917 printf("[%s][version %u] run %s.\n", __func__, (unsigned)version, (res) ? "SUCCESS" : "FAILED");
918 free_padding(rgb, padding);
919 free_yuv420(luma, padding);
920 free_yuv420(chroma, padding);
921 free_yuv420(lumaGeneric, padding);
922 free_yuv420(chromaGeneric, padding);
923 return res;
924}
925
926static BOOL run_tests(prim_size_t roi)
927{
928 BOOL rc = FALSE;
929 for (UINT32 type = PRIMITIVES_PURE_SOFT; type <= PRIMITIVES_AUTODETECT; type++)
930 {
931 primitives_t* prims = primitives_get_by_type(type);
932 if (!prims)
933 {
934 printf("primitives type %d not supported\n", type);
935 continue;
936 }
937
938 for (UINT32 x = 0; x < 5; x++)
939 {
940
941 printf("-------------------- GENERIC ------------------------\n");
942
943 if (!TestPrimitiveYUV(prims, roi, TRUE))
944 goto fail;
945
946 printf("---------------------- END --------------------------\n");
947 printf("-------------------- GENERIC ------------------------\n");
948
949 if (!TestPrimitiveYUV(prims, roi, FALSE))
950 goto fail;
951
952 printf("---------------------- END --------------------------\n");
953 printf("-------------------- GENERIC ------------------------\n");
954
955 if (!TestPrimitiveYUVCombine(prims, roi))
956 goto fail;
957
958 printf("---------------------- END --------------------------\n");
959 printf("-------------------- GENERIC ------------------------\n");
960
961 if (!TestPrimitiveRgbToLumaChroma(prims, roi, 1))
962 goto fail;
963
964 printf("---------------------- END --------------------------\n");
965 printf("-------------------- GENERIC ------------------------\n");
966
967 if (!TestPrimitiveRgbToLumaChroma(prims, roi, 2))
968 goto fail;
969
970 printf("---------------------- END --------------------------\n");
971 }
972 }
973 rc = TRUE;
974fail:
975 printf("[%s] run %s.\n", __func__, (rc) ? "SUCCESS" : "FAILED");
976 return rc;
977}
978
979static void free_yuv(BYTE* yuv[3])
980{
981 for (size_t x = 0; x < 3; x++)
982 {
983 free(yuv[x]);
984 yuv[x] = nullptr;
985 }
986}
987
988static BOOL allocate_yuv(BYTE* yuv[3], prim_size_t roi)
989{
990 yuv[0] = calloc(roi.width, roi.height);
991 yuv[1] = calloc(roi.width, roi.height);
992 yuv[2] = calloc(roi.width, roi.height);
993
994 if (!yuv[0] || !yuv[1] || !yuv[2])
995 {
996 free_yuv(yuv);
997 return FALSE;
998 }
999
1000 if (winpr_RAND(yuv[0], 1ULL * roi.width * roi.height) < 0)
1001 return FALSE;
1002 if (winpr_RAND(yuv[1], 1ULL * roi.width * roi.height) < 0)
1003 return FALSE;
1004 if (winpr_RAND(yuv[2], 1ULL * roi.width * roi.height) < 0)
1005 return FALSE;
1006 return TRUE;
1007}
1008
1009static BOOL yuv444_to_rgb(BYTE* rgb, size_t stride, const BYTE* yuv[3], const UINT32 yuvStep[3],
1010 prim_size_t roi)
1011{
1012 for (size_t y = 0; y < roi.height; y++)
1013 {
1014 const BYTE* yline[3] = {
1015 yuv[0] + y * roi.width,
1016 yuv[1] + y * roi.width,
1017 yuv[2] + y * roi.width,
1018 };
1019 BYTE* line = &rgb[y * stride];
1020
1021 for (size_t x = 0; x < roi.width; x++)
1022 {
1023 const BYTE Y = yline[0][x];
1024 const BYTE U = yline[1][x];
1025 const BYTE V = yline[2][x];
1026
1027 writeYUVPixel(&line[x * 4], PIXEL_FORMAT_BGRX32, Y, U, V, writePixelBGRX);
1028 }
1029 }
1030}
1031
1032/* Check the result of generic matches the optimized routine.
1033 *
1034 */
1035static BOOL compare_yuv444_to_rgb(prim_size_t roi, DWORD type)
1036{
1037 BOOL rc = FALSE;
1038 const UINT32 format = PIXEL_FORMAT_BGRA32;
1039 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1040 const UINT32 yuvStep[3] = { roi.width, roi.width, roi.width };
1041 const size_t stride = 4ULL * roi.width;
1042
1043 primitives_t* prims = primitives_get_by_type(type);
1044 if (!prims)
1045 {
1046 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1047 return TRUE;
1048 }
1049
1050 BYTE* rgb1 = calloc(roi.height, stride);
1051 BYTE* rgb2 = calloc(roi.height, stride);
1052
1053 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1054 if (!soft)
1055 goto fail;
1056 if (!allocate_yuv(yuv, roi) || !rgb1 || !rgb2)
1057 goto fail;
1058
1059 const BYTE* cyuv[] = { yuv[0], yuv[1], yuv[2] };
1060 if (soft->YUV444ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb1, stride, format, &roi) !=
1061 PRIMITIVES_SUCCESS)
1062 goto fail;
1063 if (prims->YUV444ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb2, stride, format, &roi) !=
1064 PRIMITIVES_SUCCESS)
1065 goto fail;
1066
1067 for (size_t y = 0; y < roi.height; y++)
1068 {
1069 const BYTE* yline[3] = {
1070 yuv[0] + y * roi.width,
1071 yuv[1] + y * roi.width,
1072 yuv[2] + y * roi.width,
1073 };
1074 const BYTE* line1 = &rgb1[y * stride];
1075 const BYTE* line2 = &rgb2[y * stride];
1076
1077 for (size_t x = 0; x < roi.width; x++)
1078 {
1079 const int Y = yline[0][x];
1080 const int U = yline[1][x];
1081 const int V = yline[2][x];
1082 const UINT32 color1 = FreeRDPReadColor(&line1[x * 4], format);
1083 const UINT32 color2 = FreeRDPReadColor(&line2[x * 4], format);
1084
1085 BYTE r1 = 0;
1086 BYTE g1 = 0;
1087 BYTE b1 = 0;
1088 FreeRDPSplitColor(color1, format, &r1, &g1, &b1, nullptr, nullptr);
1089
1090 BYTE r2 = 0;
1091 BYTE g2 = 0;
1092 BYTE b2 = 0;
1093 FreeRDPSplitColor(color2, format, &r2, &g2, &b2, nullptr, nullptr);
1094
1095 const int dr12 = abs(r1 - r2);
1096 const int dg12 = abs(g1 - g2);
1097 const int db12 = abs(b1 - b2);
1098
1099 if ((dr12 != 0) || (dg12 != 0) || (db12 != 0))
1100 {
1101 printf("{\n");
1102 printf("\tdiff 1/2: yuv {%d, %d, %d}, rgb {%d, %d, %d}\n", Y, U, V, dr12, dg12,
1103 db12);
1104 printf("}\n");
1105 }
1106
1107 if ((dr12 > 0) || (dg12 > 0) || (db12 > 0))
1108 {
1109 (void)fprintf(stderr,
1110 "[%" PRIuz "x%" PRIuz
1111 "] generic and optimized data mismatch: r[0x%" PRIx8 "|0x%" PRIx8
1112 "] g[0x%" PRIx8 "|0x%" PRIx8 "] b[0x%" PRIx8 "|0x%" PRIx8 "]\n",
1113 x, y, r1, r2, g1, g2, b1, b2);
1114 (void)fprintf(stderr, "roi: %dx%d\n", roi.width, roi.height);
1115 winpr_HexDump("y0", WLOG_INFO, &yline[0][x], 16);
1116 winpr_HexDump("y1", WLOG_INFO, &yline[0][x + roi.width], 16);
1117 winpr_HexDump("u0", WLOG_INFO, &yline[1][x], 16);
1118 winpr_HexDump("u1", WLOG_INFO, &yline[1][x + roi.width], 16);
1119 winpr_HexDump("v0", WLOG_INFO, &yline[2][x], 16);
1120 winpr_HexDump("v1", WLOG_INFO, &yline[2][x + roi.width], 16);
1121 winpr_HexDump("foo1", WLOG_INFO, &line1[x * 4], 16);
1122 winpr_HexDump("foo2", WLOG_INFO, &line2[x * 4], 16);
1123 goto fail;
1124 }
1125 }
1126 }
1127
1128 rc = TRUE;
1129fail:
1130 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1131 free_yuv(yuv);
1132 free(rgb1);
1133 free(rgb2);
1134
1135 return rc;
1136}
1137
1138/* Check the result of generic matches the optimized routine.
1139 *
1140 */
1141static BOOL compare_rgb_to_yuv444(prim_size_t roi, DWORD type)
1142{
1143 BOOL rc = FALSE;
1144 const UINT32 format = PIXEL_FORMAT_BGRA32;
1145 const size_t stride = 4ULL * roi.width;
1146 const UINT32 yuvStep[] = { roi.width, roi.width, roi.width };
1147 BYTE* yuv1[3] = WINPR_C_ARRAY_INIT;
1148 BYTE* yuv2[3] = WINPR_C_ARRAY_INIT;
1149
1150 primitives_t* prims = primitives_get_by_type(type);
1151 if (!prims)
1152 {
1153 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1154 return TRUE;
1155 }
1156
1157 BYTE* rgb = calloc(roi.height, stride);
1158
1159 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1160 if (!soft || !rgb)
1161 goto fail;
1162
1163 if (!allocate_yuv(yuv1, roi) || !allocate_yuv(yuv2, roi))
1164 goto fail;
1165
1166 if (soft->RGBToYUV444_8u_P3AC4R(rgb, format, stride, yuv1, yuvStep, &roi) != PRIMITIVES_SUCCESS)
1167 goto fail;
1168 if (prims->RGBToYUV444_8u_P3AC4R(rgb, format, stride, yuv2, yuvStep, &roi) !=
1169 PRIMITIVES_SUCCESS)
1170 goto fail;
1171
1172 for (size_t y = 0; y < roi.height; y++)
1173 {
1174 const BYTE* yline1[3] = {
1175 yuv1[0] + y * roi.width,
1176 yuv1[1] + y * roi.width,
1177 yuv1[2] + y * roi.width,
1178 };
1179 const BYTE* yline2[3] = {
1180 yuv2[0] + y * roi.width,
1181 yuv2[1] + y * roi.width,
1182 yuv2[2] + y * roi.width,
1183 };
1184
1185 for (size_t x = 0; x < ARRAYSIZE(yline1); x++)
1186 {
1187 if (memcmp(yline1[x], yline2[x], yuvStep[x]) != 0)
1188 {
1189 (void)fprintf(stderr, "[%s] compare failed in line %" PRIuz, __func__, x);
1190 goto fail;
1191 }
1192 }
1193 }
1194
1195 rc = TRUE;
1196fail:
1197 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1198 free(rgb);
1199 free_yuv(yuv1);
1200 free_yuv(yuv2);
1201
1202 return rc;
1203}
1204
1205/* Check the result of generic matches the optimized routine.
1206 *
1207 */
1208static BOOL compare_yuv420_to_rgb(prim_size_t roi, DWORD type)
1209{
1210 BOOL rc = FALSE;
1211 const UINT32 format = PIXEL_FORMAT_BGRA32;
1212 BYTE* yuv[3] = WINPR_C_ARRAY_INIT;
1213 const UINT32 yuvStep[3] = { roi.width, roi.width / 2, roi.width / 2 };
1214 const size_t stride = 4ULL * roi.width;
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
1223 BYTE* rgb1 = calloc(roi.height, stride);
1224 BYTE* rgb2 = calloc(roi.height, stride);
1225
1226 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1227 if (!soft)
1228 goto fail;
1229 if (!allocate_yuv(yuv, roi) || !rgb1 || !rgb2)
1230 goto fail;
1231
1232 const BYTE* cyuv[3] = { yuv[0], yuv[1], yuv[2] };
1233 if (soft->YUV420ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb1, stride, format, &roi) !=
1234 PRIMITIVES_SUCCESS)
1235 goto fail;
1236 if (prims->YUV420ToRGB_8u_P3AC4R(cyuv, yuvStep, rgb2, stride, format, &roi) !=
1237 PRIMITIVES_SUCCESS)
1238 goto fail;
1239
1240 for (size_t y = 0; y < roi.height; y++)
1241 {
1242 const BYTE* yline[3] = {
1243 yuv[0] + y * yuvStep[0],
1244 yuv[1] + y * yuvStep[1],
1245 yuv[2] + y * yuvStep[2],
1246 };
1247 const BYTE* line1 = &rgb1[y * stride];
1248 const BYTE* line2 = &rgb2[y * stride];
1249
1250 for (size_t x = 0; x < roi.width; x++)
1251 {
1252 const int Y = yline[0][x];
1253 const int U = yline[1][x / 2];
1254 const int V = yline[2][x / 2];
1255 const UINT32 color1 = FreeRDPReadColor(&line1[x * 4], format);
1256 const UINT32 color2 = FreeRDPReadColor(&line2[x * 4], format);
1257
1258 BYTE r1 = 0;
1259 BYTE g1 = 0;
1260 BYTE b1 = 0;
1261 FreeRDPSplitColor(color1, format, &r1, &g1, &b1, nullptr, nullptr);
1262
1263 BYTE r2 = 0;
1264 BYTE g2 = 0;
1265 BYTE b2 = 0;
1266 FreeRDPSplitColor(color2, format, &r2, &g2, &b2, nullptr, nullptr);
1267
1268 const int dr12 = abs(r1 - r2);
1269 const int dg12 = abs(g1 - g2);
1270 const int db12 = abs(b1 - b2);
1271
1272 if ((dr12 != 0) || (dg12 != 0) || (db12 != 0))
1273 {
1274 printf("{\n");
1275 printf("\tdiff 1/2: yuv {%d, %d, %d}, rgb {%d, %d, %d}\n", Y, U, V, dr12, dg12,
1276 db12);
1277 printf("}\n");
1278 }
1279
1280 if ((dr12 > 0) || (dg12 > 0) || (db12 > 0))
1281 {
1282 printf("[%s] failed: r[%" PRIx8 "|%" PRIx8 "] g[%" PRIx8 "|%" PRIx8 "] b[%" PRIx8
1283 "|%" PRIx8 "]\n",
1284 __func__, r1, r2, g1, g2, b1, b2);
1285 goto fail;
1286 }
1287 }
1288 }
1289
1290 rc = TRUE;
1291fail:
1292 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1293 free_yuv(yuv);
1294 free(rgb1);
1295 free(rgb2);
1296
1297 return rc;
1298}
1299
1300static BOOL similarYUV(const BYTE* line1, const BYTE* line2, size_t len)
1301{
1302 for (size_t x = 0; x < len; x++)
1303 {
1304 const int a = line1[x];
1305 const int b = line2[x];
1306 const int diff = abs(a - b);
1307 return (diff < 2);
1308 }
1309}
1310
1311/* Due to optimizations the Y value might be off by +/- 1 */
1312static int similarY(const BYTE* a, const BYTE* b, size_t size, size_t type)
1313{
1314 switch (type)
1315 {
1316 case 0:
1317 case 1:
1318 case 2:
1319 for (size_t x = 0; x < size; x++)
1320 {
1321 const int ba = a[x];
1322 const int bb = b[x];
1323 const int diff = abs(ba - bb);
1324 if (diff > 2)
1325 return diff;
1326 }
1327 return 0;
1328 break;
1329 default:
1330 return memcmp(a, b, size);
1331 }
1332}
1333/* Check the result of generic matches the optimized routine.
1334 *
1335 */
1336static BOOL compare_rgb_to_yuv420(prim_size_t roi, DWORD type)
1337{
1338 BOOL rc = FALSE;
1339 const UINT32 format = PIXEL_FORMAT_BGRA32;
1340 const size_t stride = 4ULL * roi.width;
1341 const UINT32 yuvStep[] = { roi.width, roi.width / 2, roi.width / 2 };
1342 BYTE* yuv1[3] = WINPR_C_ARRAY_INIT;
1343 BYTE* yuv2[3] = WINPR_C_ARRAY_INIT;
1344
1345 primitives_t* prims = primitives_get_by_type(type);
1346 if (!prims)
1347 {
1348 printf("primitives type %" PRIu32 " not supported, skipping\n", type);
1349 return TRUE;
1350 }
1351
1352 BYTE* rgb = calloc(roi.height, stride);
1353 BYTE* rgbcopy = calloc(roi.height, stride);
1354
1355 primitives_t* soft = primitives_get_by_type(PRIMITIVES_PURE_SOFT);
1356 if (!soft || !rgb || !rgbcopy)
1357 goto fail;
1358
1359 if (winpr_RAND(rgb, roi.height * stride) < 0)
1360 goto fail;
1361 memcpy(rgbcopy, rgb, roi.height * stride);
1362
1363 if (!allocate_yuv(yuv1, roi) || !allocate_yuv(yuv2, roi))
1364 goto fail;
1365
1366 if (soft->RGBToYUV420_8u_P3AC4R(rgb, format, stride, yuv1, yuvStep, &roi) != PRIMITIVES_SUCCESS)
1367 goto fail;
1368 if (memcmp(rgb, rgbcopy, roi.height * stride) != 0)
1369 goto fail;
1370 if (prims->RGBToYUV420_8u_P3AC4R(rgb, format, stride, yuv2, yuvStep, &roi) !=
1371 PRIMITIVES_SUCCESS)
1372 goto fail;
1373
1374 for (size_t y = 0; y < roi.height; y++)
1375 {
1376 // odd lines do produce artefacts in last line, skip check
1377 if (((y + 1) >= roi.height) && ((y % 2) == 0))
1378 continue;
1379
1380 const BYTE* yline1[3] = {
1381 &yuv1[0][y * yuvStep[0]],
1382 &yuv1[1][(y / 2) * yuvStep[1]],
1383 &yuv1[2][(y / 2) * yuvStep[2]],
1384 };
1385 const BYTE* yline2[3] = {
1386 &yuv2[0][y * yuvStep[0]],
1387 &yuv2[1][(y / 2) * yuvStep[1]],
1388 &yuv2[2][(y / 2) * yuvStep[2]],
1389 };
1390
1391 for (size_t x = 0; x < ARRAYSIZE(yline1); x++)
1392 {
1393 if (similarY(yline1[x], yline2[x], yuvStep[x], x) != 0)
1394 {
1395 (void)fprintf(stderr,
1396 "[%s] compare failed in component %" PRIuz ", line %" PRIuz "\n",
1397 __func__, x, y);
1398 (void)fprintf(stderr, "[%s] roi %" PRIu32 "x%" PRIu32 "\n", __func__, roi.width,
1399 roi.height);
1400 winpr_HexDump(TAG, WLOG_WARN, yline1[x], yuvStep[x]);
1401 winpr_HexDump(TAG, WLOG_WARN, yline2[x], yuvStep[x]);
1402 winpr_HexDump(TAG, WLOG_WARN, &rgb[y * stride], stride);
1403 goto fail;
1404 }
1405 }
1406 }
1407
1408 rc = TRUE;
1409fail:
1410 printf("%s finished with %s\n", __func__, rc ? "SUCCESS" : "FAILURE");
1411 free(rgb);
1412 free(rgbcopy);
1413 free_yuv(yuv1);
1414 free_yuv(yuv2);
1415
1416 return rc;
1417}
1418
1419int TestPrimitivesYUV(int argc, char* argv[])
1420{
1421 BOOL large = (argc > 1);
1422 int rc = -1;
1423 WINPR_UNUSED(argc);
1424 WINPR_UNUSED(argv);
1425 prim_size_t roi = WINPR_C_ARRAY_INIT;
1426
1427 if (argc > 1)
1428 {
1429 BOOL reset = TRUE;
1430 char* str = argv[1];
1431 char* ptr = strchr(str, 'x');
1432 if (ptr)
1433 {
1434 *ptr++ = '\0';
1435
1436 errno = 0;
1437 roi.width = strtoul(str, nullptr, 0);
1438 if (errno == 0)
1439 roi.height = strtoul(str, nullptr, 0);
1440 reset = errno != 0;
1441 }
1442
1443 if (reset)
1444 {
1445 roi.width = 1920;
1446 roi.height = 1080;
1447 }
1448 }
1449 else
1450 get_size(large, &roi.width, &roi.height);
1451
1452 prim_test_setup(FALSE);
1453
1454 for (UINT32 type = PRIMITIVES_PURE_SOFT; type <= PRIMITIVES_AUTODETECT; type++)
1455 {
1456 if (!compare_yuv444_to_rgb(roi, type))
1457 goto end;
1458 if (!compare_rgb_to_yuv444(roi, type))
1459 goto end;
1460
1461 if (!compare_yuv420_to_rgb(roi, type))
1462 goto end;
1463 if (!compare_rgb_to_yuv420(roi, type))
1464 goto end;
1465 }
1466
1467 if (!run_tests(roi))
1468 goto end;
1469
1470 rc = 0;
1471end:
1472 printf("[%s] finished, status %s [%d]\n", __func__, (rc == 0) ? "SUCCESS" : "FAILURE", rc);
1473 return rc;
1474}