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