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