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