FreeRDP
TestPrimitivesYUV.c
1 
2 #include <freerdp/config.h>
3 
4 #include <math.h>
5 
6 #include "prim_test.h"
7 
8 #include <winpr/wlog.h>
9 #include <winpr/crypto.h>
10 #include <freerdp/primitives.h>
11 #include <freerdp/utils/profiler.h>
12 
13 #define TAG __FILE__
14 
15 #define PADDING_FILL_VALUE 0x37
16 
17 /* YUV to RGB conversion is lossy, so consider every value only
18  * differing by less than 2 abs equal. */
19 static BOOL similar(const BYTE* src, const BYTE* dst, size_t size)
20 {
21  for (size_t x = 0; x < size; x++)
22  {
23  int diff = src[x] - dst[x];
24 
25  if (abs(diff) > 4)
26  {
27  (void)fprintf(stderr, "%" PRIuz " %02" PRIX8 " : %02" PRIX8 " diff=%d\n", x, src[x],
28  dst[x], abs(diff));
29  return FALSE;
30  }
31  }
32 
33  return TRUE;
34 }
35 
36 static BOOL similarRGB(const BYTE* src, const BYTE* dst, size_t size, UINT32 format, BOOL use444)
37 {
38  const UINT32 bpp = FreeRDPGetBytesPerPixel(format);
39  BYTE fill = PADDING_FILL_VALUE;
40  if (!FreeRDPColorHasAlpha(format))
41  fill = 0xFF;
42 
43  for (size_t x = 0; x < size; x++)
44  {
45  const LONG maxDiff = 4;
46  UINT32 sColor = 0;
47  UINT32 dColor = 0;
48  BYTE sR = 0;
49  BYTE sG = 0;
50  BYTE sB = 0;
51  BYTE sA = 0;
52  BYTE dR = 0;
53  BYTE dG = 0;
54  BYTE dB = 0;
55  BYTE dA = 0;
56  sColor = FreeRDPReadColor(src, format);
57  dColor = FreeRDPReadColor(dst, format);
58  src += bpp;
59  dst += bpp;
60  FreeRDPSplitColor(sColor, format, &sR, &sG, &sB, &sA, NULL);
61  FreeRDPSplitColor(dColor, format, &dR, &dG, &dB, &dA, NULL);
62 
63  if ((labs(sR - dR) > maxDiff) || (labs(sG - dG) > maxDiff) || (labs(sB - dB) > maxDiff))
64  {
65  (void)fprintf(
66  stderr,
67  "Color value mismatch R[%02X %02X], G[%02X %02X], B[%02X %02X] at position %" PRIuz
68  "\n",
69  sR, dR, sG, dG, sA, dA, x);
70  return FALSE;
71  }
72 
73  if (dA != fill)
74  {
75  (void)fprintf(
76  stderr,
77  "[%s] Invalid destination alpha value 0x%02X [expected 0x%02X] at position %" PRIuz
78  "\n",
79  use444 ? "AVC444" : "AVC420", dA, fill, x);
80  return FALSE;
81  }
82  }
83 
84  return TRUE;
85 }
86 
87 static void get_size(BOOL large, UINT32* width, UINT32* height)
88 {
89  UINT32 shift = large ? 8 : 1;
90  winpr_RAND(width, sizeof(*width));
91  winpr_RAND(height, sizeof(*height));
92  // TODO: Algorithm only works on even resolutions...
93  *width = (*width % 64 + 1) << shift;
94  *height = (*height % 64 + 1) << shift;
95 }
96 
97 static BOOL check_padding(const BYTE* psrc, size_t size, size_t padding, const char* buffer)
98 {
99  BOOL rc = TRUE;
100  const BYTE* src = NULL;
101  const BYTE* esrc = NULL;
102  size_t halfPad = (padding + 1) / 2;
103 
104  if (!psrc)
105  return FALSE;
106 
107  src = psrc - halfPad;
108  esrc = src + size + halfPad;
109 
110  for (size_t x = 0; x < halfPad; x++)
111  {
112  const BYTE s = *src++;
113  const BYTE d = *esrc++;
114 
115  if (s != 'A')
116  {
117  size_t start = x;
118 
119  while ((x < halfPad) && (*esrc++ != 'A'))
120  x++;
121 
122  (void)fprintf(stderr,
123  "Buffer underflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
124  "]\n",
125  d, 'A', buffer, start, x);
126  return FALSE;
127  }
128 
129  if (d != 'A')
130  {
131  size_t start = x;
132 
133  while ((x < halfPad) && (*esrc++ != 'A'))
134  x++;
135 
136  (void)fprintf(stderr,
137  "Buffer overflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz
138  "]\n",
139  d, 'A', buffer, start, x);
140  return FALSE;
141  }
142  }
143 
144  return rc;
145 }
146 
147 static void* set_padding(size_t size, size_t padding)
148 {
149  size_t halfPad = (padding + 1) / 2;
150  BYTE* psrc = NULL;
151  BYTE* src = winpr_aligned_malloc(size + 2 * halfPad, 16);
152 
153  if (!src)
154  return NULL;
155 
156  memset(&src[0], 'A', halfPad);
157  memset(&src[halfPad], PADDING_FILL_VALUE, size);
158  memset(&src[halfPad + size], 'A', halfPad);
159  psrc = &src[halfPad];
160 
161  if (!check_padding(psrc, size, padding, "init"))
162  {
163  winpr_aligned_free(src);
164  return NULL;
165  }
166 
167  return psrc;
168 }
169 
170 static void free_padding(void* src, size_t padding)
171 {
172  BYTE* ptr = NULL;
173 
174  if (!src)
175  return;
176 
177  ptr = ((BYTE*)src) - (padding + 1) / 2;
178  winpr_aligned_free(ptr);
179 }
180 
181 /* Create 2 pseudo YUV420 frames of same size.
182  * Combine them and check, if the data is at the expected position. */
183 static BOOL TestPrimitiveYUVCombine(primitives_t* prims, prim_size_t roi)
184 {
185  union
186  {
187  const BYTE** cpv;
188  BYTE** pv;
189  } cnv;
190  size_t awidth = 0;
191  size_t aheight = 0;
192  BOOL rc = FALSE;
193  BYTE* luma[3] = { 0 };
194  BYTE* chroma[3] = { 0 };
195  BYTE* yuv[3] = { 0 };
196  BYTE* pmain[3] = { 0 };
197  BYTE* paux[3] = { 0 };
198  UINT32 lumaStride[3];
199  UINT32 chromaStride[3];
200  UINT32 yuvStride[3];
201  const size_t padding = 10000;
202  RECTANGLE_16 rect;
203  PROFILER_DEFINE(yuvCombine)
204  PROFILER_DEFINE(yuvSplit)
205  awidth = roi.width + 16 - roi.width % 16;
206  aheight = roi.height + 16 - roi.height % 16;
207  (void)fprintf(stderr,
208  "Running YUVCombine on frame size %" PRIu32 "x%" PRIu32 " [%" PRIu32 "x%" PRIu32
209  "]\n",
210  roi.width, roi.height, awidth, aheight);
211  PROFILER_CREATE(yuvCombine, "YUV420CombineToYUV444")
212  PROFILER_CREATE(yuvSplit, "YUV444SplitToYUV420")
213  rect.left = 0;
214  rect.top = 0;
215  rect.right = roi.width;
216  rect.bottom = roi.height;
217 
218  if (!prims || !prims->YUV420CombineToYUV444)
219  goto fail;
220 
221  for (UINT32 x = 0; x < 3; x++)
222  {
223  size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
224  size_t size = aheight * awidth;
225  size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
226  yuvStride[x] = awidth;
227 
228  if (!(yuv[x] = set_padding(size, padding)))
229  goto fail;
230 
231  lumaStride[x] = halfStride;
232 
233  if (!(luma[x] = set_padding(halfSize, padding)))
234  goto fail;
235 
236  if (!(pmain[x] = set_padding(halfSize, padding)))
237  goto fail;
238 
239  chromaStride[x] = halfStride;
240 
241  if (!(chroma[x] = set_padding(halfSize, padding)))
242  goto fail;
243 
244  if (!(paux[x] = set_padding(halfSize, padding)))
245  goto fail;
246 
247  memset(luma[x], 0xAB + 3 * x, halfSize);
248  memset(chroma[x], 0x80 + 2 * x, halfSize);
249 
250  if (!check_padding(luma[x], halfSize, padding, "luma"))
251  goto fail;
252 
253  if (!check_padding(chroma[x], halfSize, padding, "chroma"))
254  goto fail;
255 
256  if (!check_padding(pmain[x], halfSize, padding, "main"))
257  goto fail;
258 
259  if (!check_padding(paux[x], halfSize, padding, "aux"))
260  goto fail;
261 
262  if (!check_padding(yuv[x], size, padding, "yuv"))
263  goto fail;
264  }
265 
266  PROFILER_ENTER(yuvCombine)
267 
268  cnv.pv = luma;
269  if (prims->YUV420CombineToYUV444(AVC444_LUMA, cnv.cpv, lumaStride, roi.width, roi.height, yuv,
270  yuvStride, &rect) != PRIMITIVES_SUCCESS)
271  {
272  PROFILER_EXIT(yuvCombine)
273  goto fail;
274  }
275 
276  cnv.pv = chroma;
277  if (prims->YUV420CombineToYUV444(AVC444_CHROMAv1, cnv.cpv, chromaStride, roi.width, roi.height,
278  yuv, yuvStride, &rect) != PRIMITIVES_SUCCESS)
279  {
280  PROFILER_EXIT(yuvCombine)
281  goto fail;
282  }
283 
284  PROFILER_EXIT(yuvCombine)
285 
286  for (size_t x = 0; x < 3; x++)
287  {
288  size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
289  size_t size = 1ULL * aheight * awidth;
290  size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
291 
292  if (!check_padding(luma[x], halfSize, padding, "luma"))
293  goto fail;
294 
295  if (!check_padding(chroma[x], halfSize, padding, "chroma"))
296  goto fail;
297 
298  if (!check_padding(yuv[x], size, padding, "yuv"))
299  goto fail;
300  }
301 
302  PROFILER_ENTER(yuvSplit)
303 
304  cnv.pv = yuv;
305  if (prims->YUV444SplitToYUV420(cnv.cpv, yuvStride, pmain, lumaStride, paux, chromaStride,
306  &roi) != PRIMITIVES_SUCCESS)
307  {
308  PROFILER_EXIT(yuvSplit)
309  goto fail;
310  }
311 
312  PROFILER_EXIT(yuvSplit)
313 
314  for (UINT32 x = 0; x < 3; x++)
315  {
316  size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
317  size_t size = aheight * awidth;
318  size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
319 
320  if (!check_padding(pmain[x], halfSize, padding, "main"))
321  goto fail;
322 
323  if (!check_padding(paux[x], halfSize, padding, "aux"))
324  goto fail;
325 
326  if (!check_padding(yuv[x], size, padding, "yuv"))
327  goto fail;
328  }
329 
330  for (size_t i = 0; i < 3; i++)
331  {
332  for (size_t y = 0; y < roi.height; y++)
333  {
334  UINT32 w = roi.width;
335  UINT32 lstride = lumaStride[i];
336  UINT32 cstride = chromaStride[i];
337 
338  if (i > 0)
339  {
340  w = (roi.width + 3) / 4;
341 
342  if (roi.height > (roi.height + 1) / 2)
343  continue;
344  }
345 
346  if (!similar(luma[i] + y * lstride, pmain[i] + y * lstride, w))
347  goto fail;
348 
349  /* Need to ignore lines of destination Y plane,
350  * if the lines are not a multiple of 16
351  * as the UV planes are packed in 8 line stripes. */
352  if (i == 0)
353  {
354  /* TODO: This check is not perfect, it does not
355  * include the last V lines packed to the Y
356  * frame. */
357  UINT32 rem = roi.height % 16;
358 
359  if (y > roi.height - rem)
360  continue;
361  }
362 
363  if (!similar(chroma[i] + y * cstride, paux[i] + y * cstride, w))
364  goto fail;
365  }
366  }
367 
368  PROFILER_PRINT_HEADER
369  PROFILER_PRINT(yuvSplit)
370  PROFILER_PRINT(yuvCombine)
371  PROFILER_PRINT_FOOTER
372  rc = TRUE;
373 fail:
374  PROFILER_FREE(yuvCombine)
375  PROFILER_FREE(yuvSplit)
376 
377  for (UINT32 x = 0; x < 3; x++)
378  {
379  free_padding(yuv[x], padding);
380  free_padding(luma[x], padding);
381  free_padding(chroma[x], padding);
382  free_padding(pmain[x], padding);
383  free_padding(paux[x], padding);
384  }
385 
386  return rc;
387 }
388 
389 static BOOL TestPrimitiveYUV(primitives_t* prims, prim_size_t roi, BOOL use444)
390 {
391  union
392  {
393  const BYTE** cpv;
394  BYTE** pv;
395  } cnv;
396  BOOL res = FALSE;
397  UINT32 awidth = 0;
398  UINT32 aheight = 0;
399  BYTE* yuv[3] = { 0 };
400  UINT32 yuv_step[3];
401  BYTE* rgb = NULL;
402  BYTE* rgb_dst = NULL;
403  size_t size = 0;
404  size_t uvsize = 0;
405  size_t uvwidth = 0;
406  size_t padding = 100ULL * 16ULL;
407  UINT32 stride = 0;
408  const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
409  PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
410  PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
411  PROFILER_DEFINE(rgbToYUV420)
412  PROFILER_DEFINE(rgbToYUV444)
413  PROFILER_DEFINE(yuv420ToRGB)
414  PROFILER_DEFINE(yuv444ToRGB)
415  /* Buffers need to be 16x16 aligned. */
416  awidth = roi.width + 16 - roi.width % 16;
417  aheight = roi.height + 16 - roi.height % 16;
418  stride = 1ULL * awidth * sizeof(UINT32);
419  size = 1ULL * awidth * aheight;
420 
421  if (use444)
422  {
423  uvwidth = awidth;
424  uvsize = size;
425 
426  if (!prims || !prims->RGBToYUV444_8u_P3AC4R || !prims->YUV444ToRGB_8u_P3AC4R)
427  return FALSE;
428  }
429  else
430  {
431  uvwidth = (awidth + 1) / 2;
432  uvsize = (aheight + 1) / 2 * uvwidth;
433 
434  if (!prims || !prims->RGBToYUV420_8u_P3AC4R || !prims->YUV420ToRGB_8u_P3AC4R)
435  return FALSE;
436  }
437 
438  (void)fprintf(stderr, "Running AVC%s on frame size %" PRIu32 "x%" PRIu32 "\n",
439  use444 ? "444" : "420", roi.width, roi.height);
440 
441  /* Test RGB to YUV444 conversion and vice versa */
442  if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
443  goto fail;
444 
445  if (!(rgb_dst = set_padding(size * sizeof(UINT32), padding)))
446  goto fail;
447 
448  if (!(yuv[0] = set_padding(size, padding)))
449  goto fail;
450 
451  if (!(yuv[1] = set_padding(uvsize, padding)))
452  goto fail;
453 
454  if (!(yuv[2] = set_padding(uvsize, padding)))
455  goto fail;
456 
457  for (size_t y = 0; y < roi.height; y++)
458  {
459  BYTE* line = &rgb[y * stride];
460 
461  for (UINT32 x = 0; x < roi.width; x++)
462  {
463  line[x * 4 + 0] = 0x81;
464  line[x * 4 + 1] = 0x33;
465  line[x * 4 + 2] = 0xAB;
466  line[x * 4 + 3] = 0xFF;
467  }
468  }
469 
470  yuv_step[0] = awidth;
471  yuv_step[1] = uvwidth;
472  yuv_step[2] = uvwidth;
473 
474  for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
475  {
476  pstatus_t rc = 0;
477  const UINT32 DstFormat = formats[x];
478  printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
479  memset(rgb_dst, PADDING_FILL_VALUE, size * sizeof(UINT32));
480 
481  PROFILER_CREATE(rgbToYUV420, "RGBToYUV420")
482  PROFILER_CREATE(rgbToYUV444, "RGBToYUV444")
483  PROFILER_CREATE(yuv420ToRGB, "YUV420ToRGB")
484  PROFILER_CREATE(yuv444ToRGB, "YUV444ToRGB")
485 
486  if (use444)
487  {
488  PROFILER_ENTER(rgbToYUV444)
489  rc = prims->RGBToYUV444_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
490  PROFILER_EXIT(rgbToYUV444)
491 
492  if (rc != PRIMITIVES_SUCCESS)
493  goto loop_fail;
494 
495  PROFILER_PRINT_HEADER
496  PROFILER_PRINT(rgbToYUV444)
497  PROFILER_PRINT_FOOTER
498  }
499  else
500  {
501  PROFILER_ENTER(rgbToYUV420)
502  rc = prims->RGBToYUV420_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
503  PROFILER_EXIT(rgbToYUV420)
504 
505  if (rc != PRIMITIVES_SUCCESS)
506  goto loop_fail;
507 
508  PROFILER_PRINT_HEADER
509  PROFILER_PRINT(rgbToYUV420)
510  PROFILER_PRINT_FOOTER
511  }
512 
513  if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
514  {
515  rc = -1;
516  goto loop_fail;
517  }
518 
519  if ((!check_padding(yuv[0], size, padding, "Y")) ||
520  (!check_padding(yuv[1], uvsize, padding, "U")) ||
521  (!check_padding(yuv[2], uvsize, padding, "V")))
522  {
523  rc = -1;
524  goto loop_fail;
525  }
526 
527  cnv.pv = yuv;
528  if (use444)
529  {
530  PROFILER_ENTER(yuv444ToRGB)
531  rc = prims->YUV444ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi);
532  PROFILER_EXIT(yuv444ToRGB)
533 
534  if (rc != PRIMITIVES_SUCCESS)
535  goto loop_fail;
536 
537  loop_fail:
538  PROFILER_EXIT(yuv444ToRGB)
539  PROFILER_PRINT_HEADER
540  PROFILER_PRINT(yuv444ToRGB)
541  PROFILER_PRINT_FOOTER
542 
543  if (rc != PRIMITIVES_SUCCESS)
544  goto fail;
545  }
546  else
547  {
548  PROFILER_ENTER(yuv420ToRGB)
549 
550  if (prims->YUV420ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi) !=
551  PRIMITIVES_SUCCESS)
552  {
553  PROFILER_EXIT(yuv420ToRGB)
554  goto fail;
555  }
556 
557  PROFILER_EXIT(yuv420ToRGB)
558  PROFILER_PRINT_HEADER
559  PROFILER_PRINT(yuv420ToRGB)
560  PROFILER_PRINT_FOOTER
561  }
562 
563  if (!check_padding(rgb_dst, size * sizeof(UINT32), padding, "rgb dst"))
564  goto fail;
565 
566  if ((!check_padding(yuv[0], size, padding, "Y")) ||
567  (!check_padding(yuv[1], uvsize, padding, "U")) ||
568  (!check_padding(yuv[2], uvsize, padding, "V")))
569  goto fail;
570 
571  for (size_t y = 0; y < roi.height; y++)
572  {
573  BYTE* srgb = &rgb[y * stride];
574  BYTE* drgb = &rgb_dst[y * stride];
575 
576  if (!similarRGB(srgb, drgb, roi.width, DstFormat, use444))
577  goto fail;
578  }
579 
580  PROFILER_FREE(rgbToYUV420)
581  PROFILER_FREE(rgbToYUV444)
582  PROFILER_FREE(yuv420ToRGB)
583  PROFILER_FREE(yuv444ToRGB)
584  }
585 
586  res = TRUE;
587 fail:
588  free_padding(rgb, padding);
589  free_padding(rgb_dst, padding);
590  free_padding(yuv[0], padding);
591  free_padding(yuv[1], padding);
592  free_padding(yuv[2], padding);
593  return res;
594 }
595 
596 static BOOL allocate_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
597 {
598  const size_t size = 1ULL * width * height;
599  const size_t uvwidth = (1ULL + width) / 2;
600  const size_t uvsize = (1ULL + height) / 2 * uvwidth;
601 
602  if (!(planes[0] = set_padding(size, padding)))
603  goto fail;
604 
605  if (!(planes[1] = set_padding(uvsize, padding)))
606  goto fail;
607 
608  if (!(planes[2] = set_padding(uvsize, padding)))
609  goto fail;
610 
611  return TRUE;
612 fail:
613  free_padding(planes[0], padding);
614  free_padding(planes[1], padding);
615  free_padding(planes[2], padding);
616  return FALSE;
617 }
618 
619 static void free_yuv420(BYTE** planes, UINT32 padding)
620 {
621  if (!planes)
622  return;
623 
624  free_padding(planes[0], padding);
625  free_padding(planes[1], padding);
626  free_padding(planes[2], padding);
627  planes[0] = NULL;
628  planes[1] = NULL;
629  planes[2] = NULL;
630 }
631 static BOOL check_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
632 {
633  const size_t size = 1ULL * width * height;
634  const size_t uvwidth = (width + 1) / 2;
635  const size_t uvsize = (height + 1) / 2 * uvwidth;
636  const BOOL yOk = check_padding(planes[0], size, padding, "Y");
637  const BOOL uOk = check_padding(planes[1], uvsize, padding, "U");
638  const BOOL vOk = check_padding(planes[2], uvsize, padding, "V");
639  return (yOk && uOk && vOk);
640 }
641 
642 static BOOL check_for_mismatches(const BYTE* planeA, const BYTE* planeB, UINT32 size)
643 {
644  BOOL rc = FALSE;
645 
646  for (UINT32 x = 0; x < size; x++)
647  {
648  const BYTE a = planeA[x];
649  const BYTE b = planeB[x];
650 
651  if (fabsf((float)a - (float)b) > 2.0f)
652  {
653  rc = TRUE;
654  (void)fprintf(stderr, "[%08x] %02x != %02x\n", x, a, b);
655  }
656  }
657 
658  return rc;
659 }
660 
661 static BOOL compare_yuv420(BYTE** planesA, BYTE** planesB, UINT32 width, UINT32 height,
662  UINT32 padding)
663 {
664  BOOL rc = TRUE;
665  const size_t size = 1ULL * width * height;
666  const size_t uvwidth = (1ULL * width + 1) / 2;
667  const size_t uvsize = (1ULL * height + 1) / 2 * uvwidth;
668 
669  if (check_for_mismatches(planesA[0], planesB[0], size))
670  {
671  (void)fprintf(stderr, "Mismatch in Y planes!");
672  rc = FALSE;
673  }
674 
675  if (check_for_mismatches(planesA[1], planesB[1], uvsize))
676  {
677  (void)fprintf(stderr, "Mismatch in U planes!");
678  rc = FALSE;
679  }
680 
681  if (check_for_mismatches(planesA[2], planesB[2], uvsize))
682  {
683  (void)fprintf(stderr, "Mismatch in V planes!");
684  rc = FALSE;
685  }
686 
687  return rc;
688 }
689 
690 static UINT32 prand(UINT32 max)
691 {
692  UINT32 tmp = 0;
693  if (max <= 1)
694  return 1;
695  winpr_RAND(&tmp, sizeof(tmp));
696  return tmp % (max - 1) + 1;
697 }
698 
699 static BOOL TestPrimitiveRgbToLumaChroma(primitives_t* prims, prim_size_t roi, UINT32 version)
700 {
701  BOOL res = FALSE;
702  UINT32 awidth = 0;
703  UINT32 aheight = 0;
704  BYTE* luma[3] = { 0 };
705  BYTE* chroma[3] = { 0 };
706  BYTE* lumaGeneric[3] = { 0 };
707  BYTE* chromaGeneric[3] = { 0 };
708  UINT32 yuv_step[3];
709  BYTE* rgb = NULL;
710  size_t size = 0;
711  size_t uvwidth = 0;
712  const size_t padding = 0x1000;
713  UINT32 stride = 0;
714  __RGBToAVC444YUV_t fkt = NULL;
715  __RGBToAVC444YUV_t gen = NULL;
716  const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
717  PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
718  PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
719  PROFILER_DEFINE(rgbToYUV444)
720  PROFILER_DEFINE(rgbToYUV444opt)
721  /* Buffers need to be 16x16 aligned. */
722  awidth = roi.width;
723 
724  if (awidth % 16 != 0)
725  awidth += 16 - roi.width % 16;
726 
727  aheight = roi.height;
728 
729  if (aheight % 16 != 0)
730  aheight += 16 - roi.height % 16;
731 
732  stride = 1ULL * awidth * sizeof(UINT32);
733  size = 1ULL * awidth * aheight;
734  uvwidth = 1ULL * (awidth + 1) / 2;
735 
736  if (!prims || !generic)
737  return FALSE;
738 
739  switch (version)
740  {
741  case 1:
742  fkt = prims->RGBToAVC444YUV;
743  gen = generic->RGBToAVC444YUV;
744  break;
745 
746  case 2:
747  fkt = prims->RGBToAVC444YUVv2;
748  gen = generic->RGBToAVC444YUVv2;
749  break;
750 
751  default:
752  return FALSE;
753  }
754 
755  if (!fkt || !gen)
756  return FALSE;
757 
758  (void)fprintf(stderr, "Running AVC444 on frame size %" PRIu32 "x%" PRIu32 "\n", roi.width,
759  roi.height);
760 
761  /* Test RGB to YUV444 conversion and vice versa */
762  if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
763  goto fail;
764 
765  if (!allocate_yuv420(luma, awidth, aheight, padding))
766  goto fail;
767 
768  if (!allocate_yuv420(chroma, awidth, aheight, padding))
769  goto fail;
770 
771  if (!allocate_yuv420(lumaGeneric, awidth, aheight, padding))
772  goto fail;
773 
774  if (!allocate_yuv420(chromaGeneric, awidth, aheight, padding))
775  goto fail;
776 
777  for (size_t y = 0; y < roi.height; y++)
778  {
779  BYTE* line = &rgb[y * stride];
780 
781  for (UINT32 x = 0; x < roi.width; x++)
782  {
783 #if 1
784  line[x * 4 + 0] = prand(UINT8_MAX);
785  line[x * 4 + 1] = prand(UINT8_MAX);
786  line[x * 4 + 2] = prand(UINT8_MAX);
787  line[x * 4 + 3] = prand(UINT8_MAX);
788 #else
789  line[x * 4 + 0] = (y * roi.width + x) * 16 + 5;
790  line[x * 4 + 1] = (y * roi.width + x) * 16 + 7;
791  line[x * 4 + 2] = (y * roi.width + x) * 16 + 11;
792  line[x * 4 + 3] = (y * roi.width + x) * 16 + 0;
793 #endif
794  }
795  }
796 
797  yuv_step[0] = awidth;
798  yuv_step[1] = uvwidth;
799  yuv_step[2] = uvwidth;
800 
801  for (UINT32 x = 0; x < sizeof(formats) / sizeof(formats[0]); x++)
802  {
803  pstatus_t rc = -1;
804  const UINT32 DstFormat = formats[x];
805  printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
806  PROFILER_CREATE(rgbToYUV444, "RGBToYUV444-generic")
807  PROFILER_CREATE(rgbToYUV444opt, "RGBToYUV444-optimized")
808 
809  for (UINT32 cnt = 0; cnt < 10; cnt++)
810  {
811  PROFILER_ENTER(rgbToYUV444opt)
812  rc = fkt(rgb, DstFormat, stride, luma, yuv_step, chroma, yuv_step, &roi);
813  PROFILER_EXIT(rgbToYUV444opt)
814 
815  if (rc != PRIMITIVES_SUCCESS)
816  goto loop_fail;
817  }
818 
819  PROFILER_PRINT_HEADER
820  PROFILER_PRINT(rgbToYUV444opt)
821  PROFILER_PRINT_FOOTER
822 
823  if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
824  {
825  rc = -1;
826  goto loop_fail;
827  }
828 
829  if (!check_yuv420(luma, awidth, aheight, padding) ||
830  !check_yuv420(chroma, awidth, aheight, padding))
831  {
832  rc = -1;
833  goto loop_fail;
834  }
835 
836  for (UINT32 cnt = 0; cnt < 10; cnt++)
837  {
838  PROFILER_ENTER(rgbToYUV444)
839  rc = gen(rgb, DstFormat, stride, lumaGeneric, yuv_step, chromaGeneric, yuv_step, &roi);
840  PROFILER_EXIT(rgbToYUV444)
841 
842  if (rc != PRIMITIVES_SUCCESS)
843  goto loop_fail;
844  }
845 
846  PROFILER_PRINT_HEADER
847  PROFILER_PRINT(rgbToYUV444)
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(lumaGeneric, awidth, aheight, padding) ||
857  !check_yuv420(chromaGeneric, awidth, aheight, padding))
858  {
859  rc = -1;
860  goto loop_fail;
861  }
862 
863  if (!compare_yuv420(luma, lumaGeneric, awidth, aheight, padding) ||
864  !compare_yuv420(chroma, chromaGeneric, awidth, aheight, padding))
865  {
866  rc = -1;
867  goto loop_fail;
868  }
869 
870  loop_fail:
871  PROFILER_FREE(rgbToYUV444)
872  PROFILER_FREE(rgbToYUV444opt)
873 
874  if (rc != PRIMITIVES_SUCCESS)
875  goto fail;
876  }
877 
878  res = TRUE;
879 fail:
880  free_padding(rgb, padding);
881  free_yuv420(luma, padding);
882  free_yuv420(chroma, padding);
883  free_yuv420(lumaGeneric, padding);
884  free_yuv420(chromaGeneric, padding);
885  return res;
886 }
887 
888 int TestPrimitivesYUV(int argc, char* argv[])
889 {
890  BOOL large = (argc > 1);
891  int rc = -1;
892  WINPR_UNUSED(argc);
893  WINPR_UNUSED(argv);
894  prim_test_setup(FALSE);
895  primitives_t* prims = primitives_get();
896 
897  for (UINT32 x = 0; x < 5; x++)
898  {
899  prim_size_t roi = { 0 };
900 
901  if (argc > 1)
902  {
903  // NOLINTNEXTLINE(cert-err34-c)
904  int crc = sscanf(argv[1], "%" PRIu32 "x%" PRIu32, &roi.width, &roi.height);
905 
906  if (crc != 2)
907  {
908  roi.width = 1920;
909  roi.height = 1080;
910  }
911  }
912  else
913  get_size(large, &roi.width, &roi.height);
914 
915  printf("-------------------- GENERIC ------------------------\n");
916 
917  if (!TestPrimitiveYUV(generic, roi, TRUE))
918  {
919  printf("TestPrimitiveYUV (444) failed.\n");
920  goto end;
921  }
922 
923  printf("---------------------- END --------------------------\n");
924  printf("------------------- OPTIMIZED -----------------------\n");
925 
926  if (!TestPrimitiveYUV(prims, roi, TRUE))
927  {
928  printf("TestPrimitiveYUV (444) failed.\n");
929  goto end;
930  }
931 
932  printf("---------------------- END --------------------------\n");
933  printf("-------------------- GENERIC ------------------------\n");
934 
935  if (!TestPrimitiveYUV(generic, roi, FALSE))
936  {
937  printf("TestPrimitiveYUV (420) failed.\n");
938  goto end;
939  }
940 
941  printf("---------------------- END --------------------------\n");
942  printf("------------------- OPTIMIZED -----------------------\n");
943 
944  if (!TestPrimitiveYUV(prims, roi, FALSE))
945  {
946  printf("TestPrimitiveYUV (420) failed.\n");
947  goto end;
948  }
949 
950  printf("---------------------- END --------------------------\n");
951  printf("-------------------- GENERIC ------------------------\n");
952 
953  if (!TestPrimitiveYUVCombine(generic, roi))
954  {
955  printf("TestPrimitiveYUVCombine failed.\n");
956  goto end;
957  }
958 
959  printf("---------------------- END --------------------------\n");
960  printf("------------------- OPTIMIZED -----------------------\n");
961 
962  if (!TestPrimitiveYUVCombine(prims, roi))
963  {
964  printf("TestPrimitiveYUVCombine failed.\n");
965  goto end;
966  }
967 
968  printf("---------------------- END --------------------------\n");
969  printf("------------------- OPTIMIZED -----------------------\n");
970 
971  if (!TestPrimitiveRgbToLumaChroma(prims, roi, 1))
972  {
973  printf("TestPrimitiveRgbToLumaChroma failed.\n");
974  goto end;
975  }
976 
977  printf("---------------------- END --------------------------\n");
978  printf("-------------------- GENERIC ------------------------\n");
979 
980  if (!TestPrimitiveRgbToLumaChroma(prims, roi, 2))
981  {
982  printf("TestPrimitiveYUVCombine failed.\n");
983  goto end;
984  }
985 
986  printf("---------------------- END --------------------------\n");
987  }
988 
989  rc = 0;
990 end:
991  return rc;
992 }