summaryrefslogtreecommitdiffstats
path: root/libfreerdp/primitives/test/TestPrimitivesYUV.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /libfreerdp/primitives/test/TestPrimitivesYUV.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libfreerdp/primitives/test/TestPrimitivesYUV.c')
-rw-r--r--libfreerdp/primitives/test/TestPrimitivesYUV.c979
1 files changed, 979 insertions, 0 deletions
diff --git a/libfreerdp/primitives/test/TestPrimitivesYUV.c b/libfreerdp/primitives/test/TestPrimitivesYUV.c
new file mode 100644
index 0000000..f679c07
--- /dev/null
+++ b/libfreerdp/primitives/test/TestPrimitivesYUV.c
@@ -0,0 +1,979 @@
+
+#include <freerdp/config.h>
+
+#include <math.h>
+
+#include "prim_test.h"
+
+#include <winpr/wlog.h>
+#include <winpr/crypto.h>
+#include <freerdp/primitives.h>
+#include <freerdp/utils/profiler.h>
+
+#define TAG __FILE__
+
+#define PADDING_FILL_VALUE 0x37
+
+/* YUV to RGB conversion is lossy, so consider every value only
+ * differing by less than 2 abs equal. */
+static BOOL similar(const BYTE* src, const BYTE* dst, size_t size)
+{
+ for (size_t x = 0; x < size; x++)
+ {
+ int diff = src[x] - dst[x];
+
+ if (abs(diff) > 4)
+ {
+ fprintf(stderr, "%" PRIuz " %02" PRIX8 " : %02" PRIX8 " diff=%d\n", x, src[x], dst[x],
+ abs(diff));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL similarRGB(const BYTE* src, const BYTE* dst, size_t size, UINT32 format, BOOL use444)
+{
+ const UINT32 bpp = FreeRDPGetBytesPerPixel(format);
+ BYTE fill = PADDING_FILL_VALUE;
+ if (!FreeRDPColorHasAlpha(format))
+ fill = 0xFF;
+
+ for (size_t x = 0; x < size; x++)
+ {
+ const LONG maxDiff = 4;
+ UINT32 sColor = 0;
+ UINT32 dColor = 0;
+ BYTE sR = 0;
+ BYTE sG = 0;
+ BYTE sB = 0;
+ BYTE sA = 0;
+ BYTE dR = 0;
+ BYTE dG = 0;
+ BYTE dB = 0;
+ BYTE dA = 0;
+ sColor = FreeRDPReadColor(src, format);
+ dColor = FreeRDPReadColor(dst, format);
+ src += bpp;
+ dst += bpp;
+ FreeRDPSplitColor(sColor, format, &sR, &sG, &sB, &sA, NULL);
+ FreeRDPSplitColor(dColor, format, &dR, &dG, &dB, &dA, NULL);
+
+ if ((labs(sR - dR) > maxDiff) || (labs(sG - dG) > maxDiff) || (labs(sB - dB) > maxDiff))
+ {
+ fprintf(
+ stderr,
+ "Color value mismatch R[%02X %02X], G[%02X %02X], B[%02X %02X] at position %" PRIuz
+ "\n",
+ sR, dR, sG, dG, sA, dA, x);
+ return FALSE;
+ }
+
+ if (dA != fill)
+ {
+ fprintf(
+ stderr,
+ "[%s] Invalid destination alpha value 0x%02X [expected 0x%02X] at position %" PRIuz
+ "\n",
+ use444 ? "AVC444" : "AVC420", dA, fill, x);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void get_size(BOOL large, UINT32* width, UINT32* height)
+{
+ UINT32 shift = large ? 8 : 1;
+ winpr_RAND(width, sizeof(*width));
+ winpr_RAND(height, sizeof(*height));
+ // TODO: Algorithm only works on even resolutions...
+ *width = (*width % 64 + 1) << shift;
+ *height = (*height % 64 + 1) << shift;
+}
+
+static BOOL check_padding(const BYTE* psrc, size_t size, size_t padding, const char* buffer)
+{
+ BOOL rc = TRUE;
+ const BYTE* src = NULL;
+ const BYTE* esrc = NULL;
+ size_t halfPad = (padding + 1) / 2;
+
+ if (!psrc)
+ return FALSE;
+
+ src = psrc - halfPad;
+ esrc = src + size + halfPad;
+
+ for (size_t x = 0; x < halfPad; x++)
+ {
+ const BYTE s = *src++;
+ const BYTE d = *esrc++;
+
+ if (s != 'A')
+ {
+ size_t start = x;
+
+ while ((x < halfPad) && (*esrc++ != 'A'))
+ x++;
+
+ fprintf(stderr,
+ "Buffer underflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz "]\n",
+ d, 'A', buffer, start, x);
+ return FALSE;
+ }
+
+ if (d != 'A')
+ {
+ size_t start = x;
+
+ while ((x < halfPad) && (*esrc++ != 'A'))
+ x++;
+
+ fprintf(stderr,
+ "Buffer overflow detected %02" PRIx8 " != %02X %s [%" PRIuz "-%" PRIuz "]\n", d,
+ 'A', buffer, start, x);
+ return FALSE;
+ }
+ }
+
+ return rc;
+}
+
+static void* set_padding(size_t size, size_t padding)
+{
+ size_t halfPad = (padding + 1) / 2;
+ BYTE* psrc = NULL;
+ BYTE* src = winpr_aligned_malloc(size + 2 * halfPad, 16);
+
+ if (!src)
+ return NULL;
+
+ memset(&src[0], 'A', halfPad);
+ memset(&src[halfPad], PADDING_FILL_VALUE, size);
+ memset(&src[halfPad + size], 'A', halfPad);
+ psrc = &src[halfPad];
+
+ if (!check_padding(psrc, size, padding, "init"))
+ {
+ winpr_aligned_free(src);
+ return NULL;
+ }
+
+ return psrc;
+}
+
+static void free_padding(void* src, size_t padding)
+{
+ BYTE* ptr = NULL;
+
+ if (!src)
+ return;
+
+ ptr = ((BYTE*)src) - (padding + 1) / 2;
+ winpr_aligned_free(ptr);
+}
+
+/* Create 2 pseudo YUV420 frames of same size.
+ * Combine them and check, if the data is at the expected position. */
+static BOOL TestPrimitiveYUVCombine(primitives_t* prims, prim_size_t roi)
+{
+ union
+ {
+ const BYTE** cpv;
+ BYTE** pv;
+ } cnv;
+ UINT32 awidth = 0;
+ UINT32 aheight = 0;
+ BOOL rc = FALSE;
+ BYTE* luma[3] = { 0 };
+ BYTE* chroma[3] = { 0 };
+ BYTE* yuv[3] = { 0 };
+ BYTE* pmain[3] = { 0 };
+ BYTE* paux[3] = { 0 };
+ UINT32 lumaStride[3];
+ UINT32 chromaStride[3];
+ UINT32 yuvStride[3];
+ const size_t padding = 10000;
+ RECTANGLE_16 rect;
+ PROFILER_DEFINE(yuvCombine)
+ PROFILER_DEFINE(yuvSplit)
+ awidth = roi.width + 16 - roi.width % 16;
+ aheight = roi.height + 16 - roi.height % 16;
+ fprintf(stderr,
+ "Running YUVCombine on frame size %" PRIu32 "x%" PRIu32 " [%" PRIu32 "x%" PRIu32 "]\n",
+ roi.width, roi.height, awidth, aheight);
+ PROFILER_CREATE(yuvCombine, "YUV420CombineToYUV444")
+ PROFILER_CREATE(yuvSplit, "YUV444SplitToYUV420")
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = roi.width;
+ rect.bottom = roi.height;
+
+ if (!prims || !prims->YUV420CombineToYUV444)
+ goto fail;
+
+ for (UINT32 x = 0; x < 3; x++)
+ {
+ size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
+ size_t size = aheight * awidth;
+ size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
+ yuvStride[x] = awidth;
+
+ if (!(yuv[x] = set_padding(size, padding)))
+ goto fail;
+
+ lumaStride[x] = halfStride;
+
+ if (!(luma[x] = set_padding(halfSize, padding)))
+ goto fail;
+
+ if (!(pmain[x] = set_padding(halfSize, padding)))
+ goto fail;
+
+ chromaStride[x] = halfStride;
+
+ if (!(chroma[x] = set_padding(halfSize, padding)))
+ goto fail;
+
+ if (!(paux[x] = set_padding(halfSize, padding)))
+ goto fail;
+
+ memset(luma[x], 0xAB + 3 * x, halfSize);
+ memset(chroma[x], 0x80 + 2 * x, halfSize);
+
+ if (!check_padding(luma[x], halfSize, padding, "luma"))
+ goto fail;
+
+ if (!check_padding(chroma[x], halfSize, padding, "chroma"))
+ goto fail;
+
+ if (!check_padding(pmain[x], halfSize, padding, "main"))
+ goto fail;
+
+ if (!check_padding(paux[x], halfSize, padding, "aux"))
+ goto fail;
+
+ if (!check_padding(yuv[x], size, padding, "yuv"))
+ goto fail;
+ }
+
+ PROFILER_ENTER(yuvCombine)
+
+ cnv.pv = luma;
+ if (prims->YUV420CombineToYUV444(AVC444_LUMA, cnv.cpv, lumaStride, roi.width, roi.height, yuv,
+ yuvStride, &rect) != PRIMITIVES_SUCCESS)
+ {
+ PROFILER_EXIT(yuvCombine)
+ goto fail;
+ }
+
+ cnv.pv = chroma;
+ if (prims->YUV420CombineToYUV444(AVC444_CHROMAv1, cnv.cpv, chromaStride, roi.width, roi.height,
+ yuv, yuvStride, &rect) != PRIMITIVES_SUCCESS)
+ {
+ PROFILER_EXIT(yuvCombine)
+ goto fail;
+ }
+
+ PROFILER_EXIT(yuvCombine)
+
+ for (UINT32 x = 0; x < 3; x++)
+ {
+ size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
+ size_t size = aheight * awidth;
+ size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
+
+ if (!check_padding(luma[x], halfSize, padding, "luma"))
+ goto fail;
+
+ if (!check_padding(chroma[x], halfSize, padding, "chroma"))
+ goto fail;
+
+ if (!check_padding(yuv[x], size, padding, "yuv"))
+ goto fail;
+ }
+
+ PROFILER_ENTER(yuvSplit)
+
+ cnv.pv = yuv;
+ if (prims->YUV444SplitToYUV420(cnv.cpv, yuvStride, pmain, lumaStride, paux, chromaStride,
+ &roi) != PRIMITIVES_SUCCESS)
+ {
+ PROFILER_EXIT(yuvSplit)
+ goto fail;
+ }
+
+ PROFILER_EXIT(yuvSplit)
+
+ for (UINT32 x = 0; x < 3; x++)
+ {
+ size_t halfStride = ((x > 0) ? awidth / 2 : awidth);
+ size_t size = aheight * awidth;
+ size_t halfSize = ((x > 0) ? halfStride * aheight / 2 : awidth * aheight);
+
+ if (!check_padding(pmain[x], halfSize, padding, "main"))
+ goto fail;
+
+ if (!check_padding(paux[x], halfSize, padding, "aux"))
+ goto fail;
+
+ if (!check_padding(yuv[x], size, padding, "yuv"))
+ goto fail;
+ }
+
+ for (UINT32 i = 0; i < 3; i++)
+ {
+ for (UINT32 y = 0; y < roi.height; y++)
+ {
+ UINT32 w = roi.width;
+ UINT32 lstride = lumaStride[i];
+ UINT32 cstride = chromaStride[i];
+
+ if (i > 0)
+ {
+ w = (roi.width + 3) / 4;
+
+ if (roi.height > (roi.height + 1) / 2)
+ continue;
+ }
+
+ if (!similar(luma[i] + y * lstride, pmain[i] + y * lstride, w))
+ goto fail;
+
+ /* Need to ignore lines of destination Y plane,
+ * if the lines are not a multiple of 16
+ * as the UV planes are packed in 8 line stripes. */
+ if (i == 0)
+ {
+ /* TODO: This check is not perfect, it does not
+ * include the last V lines packed to the Y
+ * frame. */
+ UINT32 rem = roi.height % 16;
+
+ if (y > roi.height - rem)
+ continue;
+ }
+
+ if (!similar(chroma[i] + y * cstride, paux[i] + y * cstride, w))
+ goto fail;
+ }
+ }
+
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(yuvSplit)
+ PROFILER_PRINT(yuvCombine)
+ PROFILER_PRINT_FOOTER
+ rc = TRUE;
+fail:
+ PROFILER_FREE(yuvCombine)
+ PROFILER_FREE(yuvSplit)
+
+ for (UINT32 x = 0; x < 3; x++)
+ {
+ free_padding(yuv[x], padding);
+ free_padding(luma[x], padding);
+ free_padding(chroma[x], padding);
+ free_padding(pmain[x], padding);
+ free_padding(paux[x], padding);
+ }
+
+ return rc;
+}
+
+static BOOL TestPrimitiveYUV(primitives_t* prims, prim_size_t roi, BOOL use444)
+{
+ union
+ {
+ const BYTE** cpv;
+ BYTE** pv;
+ } cnv;
+ BOOL res = FALSE;
+ UINT32 awidth = 0;
+ UINT32 aheight = 0;
+ BYTE* yuv[3] = { 0 };
+ UINT32 yuv_step[3];
+ BYTE* rgb = NULL;
+ BYTE* rgb_dst = NULL;
+ size_t size = 0;
+ size_t uvsize = 0;
+ size_t uvwidth = 0;
+ size_t padding = 100 * 16;
+ UINT32 stride = 0;
+ const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
+ PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
+ PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
+ PROFILER_DEFINE(rgbToYUV420)
+ PROFILER_DEFINE(rgbToYUV444)
+ PROFILER_DEFINE(yuv420ToRGB)
+ PROFILER_DEFINE(yuv444ToRGB)
+ /* Buffers need to be 16x16 aligned. */
+ awidth = roi.width + 16 - roi.width % 16;
+ aheight = roi.height + 16 - roi.height % 16;
+ stride = awidth * sizeof(UINT32);
+ size = awidth * aheight;
+
+ if (use444)
+ {
+ uvwidth = awidth;
+ uvsize = size;
+
+ if (!prims || !prims->RGBToYUV444_8u_P3AC4R || !prims->YUV444ToRGB_8u_P3AC4R)
+ return FALSE;
+ }
+ else
+ {
+ uvwidth = (awidth + 1) / 2;
+ uvsize = (aheight + 1) / 2 * uvwidth;
+
+ if (!prims || !prims->RGBToYUV420_8u_P3AC4R || !prims->YUV420ToRGB_8u_P3AC4R)
+ return FALSE;
+ }
+
+ fprintf(stderr, "Running AVC%s on frame size %" PRIu32 "x%" PRIu32 "\n", use444 ? "444" : "420",
+ roi.width, roi.height);
+
+ /* Test RGB to YUV444 conversion and vice versa */
+ if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
+ goto fail;
+
+ if (!(rgb_dst = set_padding(size * sizeof(UINT32), padding)))
+ goto fail;
+
+ if (!(yuv[0] = set_padding(size, padding)))
+ goto fail;
+
+ if (!(yuv[1] = set_padding(uvsize, padding)))
+ goto fail;
+
+ if (!(yuv[2] = set_padding(uvsize, padding)))
+ goto fail;
+
+ for (UINT32 y = 0; y < roi.height; y++)
+ {
+ BYTE* line = &rgb[y * stride];
+
+ for (UINT32 x = 0; x < roi.width; x++)
+ {
+ line[x * 4 + 0] = 0x81;
+ line[x * 4 + 1] = 0x33;
+ line[x * 4 + 2] = 0xAB;
+ line[x * 4 + 3] = 0xFF;
+ }
+ }
+
+ yuv_step[0] = awidth;
+ yuv_step[1] = uvwidth;
+ yuv_step[2] = uvwidth;
+
+ for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
+ {
+ pstatus_t rc = 0;
+ const UINT32 DstFormat = formats[x];
+ printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
+ memset(rgb_dst, PADDING_FILL_VALUE, size * sizeof(UINT32));
+
+ PROFILER_CREATE(rgbToYUV420, "RGBToYUV420")
+ PROFILER_CREATE(rgbToYUV444, "RGBToYUV444")
+ PROFILER_CREATE(yuv420ToRGB, "YUV420ToRGB")
+ PROFILER_CREATE(yuv444ToRGB, "YUV444ToRGB")
+
+ if (use444)
+ {
+ PROFILER_ENTER(rgbToYUV444)
+ rc = prims->RGBToYUV444_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
+ PROFILER_EXIT(rgbToYUV444)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto loop_fail;
+
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(rgbToYUV444)
+ PROFILER_PRINT_FOOTER
+ }
+ else
+ {
+ PROFILER_ENTER(rgbToYUV420)
+ rc = prims->RGBToYUV420_8u_P3AC4R(rgb, DstFormat, stride, yuv, yuv_step, &roi);
+ PROFILER_EXIT(rgbToYUV420)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto loop_fail;
+
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(rgbToYUV420)
+ PROFILER_PRINT_FOOTER
+ }
+
+ if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ if ((!check_padding(yuv[0], size, padding, "Y")) ||
+ (!check_padding(yuv[1], uvsize, padding, "U")) ||
+ (!check_padding(yuv[2], uvsize, padding, "V")))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ cnv.pv = yuv;
+ if (use444)
+ {
+ PROFILER_ENTER(yuv444ToRGB)
+ rc = prims->YUV444ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi);
+ PROFILER_EXIT(yuv444ToRGB)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto loop_fail;
+
+ loop_fail:
+ PROFILER_EXIT(yuv444ToRGB)
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(yuv444ToRGB)
+ PROFILER_PRINT_FOOTER
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto fail;
+ }
+ else
+ {
+ PROFILER_ENTER(yuv420ToRGB)
+
+ if (prims->YUV420ToRGB_8u_P3AC4R(cnv.cpv, yuv_step, rgb_dst, stride, DstFormat, &roi) !=
+ PRIMITIVES_SUCCESS)
+ {
+ PROFILER_EXIT(yuv420ToRGB)
+ goto fail;
+ }
+
+ PROFILER_EXIT(yuv420ToRGB)
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(yuv420ToRGB)
+ PROFILER_PRINT_FOOTER
+ }
+
+ if (!check_padding(rgb_dst, size * sizeof(UINT32), padding, "rgb dst"))
+ goto fail;
+
+ if ((!check_padding(yuv[0], size, padding, "Y")) ||
+ (!check_padding(yuv[1], uvsize, padding, "U")) ||
+ (!check_padding(yuv[2], uvsize, padding, "V")))
+ goto fail;
+
+ for (UINT32 y = 0; y < roi.height; y++)
+ {
+ BYTE* srgb = &rgb[y * stride];
+ BYTE* drgb = &rgb_dst[y * stride];
+
+ if (!similarRGB(srgb, drgb, roi.width, DstFormat, use444))
+ goto fail;
+ }
+
+ PROFILER_FREE(rgbToYUV420)
+ PROFILER_FREE(rgbToYUV444)
+ PROFILER_FREE(yuv420ToRGB)
+ PROFILER_FREE(yuv444ToRGB)
+ }
+
+ res = TRUE;
+fail:
+ free_padding(rgb, padding);
+ free_padding(rgb_dst, padding);
+ free_padding(yuv[0], padding);
+ free_padding(yuv[1], padding);
+ free_padding(yuv[2], padding);
+ return res;
+}
+
+static BOOL allocate_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
+{
+ const size_t size = width * height;
+ const size_t uvwidth = (width + 1) / 2;
+ const size_t uvsize = (height + 1) / 2 * uvwidth;
+
+ if (!(planes[0] = set_padding(size, padding)))
+ goto fail;
+
+ if (!(planes[1] = set_padding(uvsize, padding)))
+ goto fail;
+
+ if (!(planes[2] = set_padding(uvsize, padding)))
+ goto fail;
+
+ return TRUE;
+fail:
+ free_padding(planes[0], padding);
+ free_padding(planes[1], padding);
+ free_padding(planes[2], padding);
+ return FALSE;
+}
+
+static void free_yuv420(BYTE** planes, UINT32 padding)
+{
+ if (!planes)
+ return;
+
+ free_padding(planes[0], padding);
+ free_padding(planes[1], padding);
+ free_padding(planes[2], padding);
+ planes[0] = NULL;
+ planes[1] = NULL;
+ planes[2] = NULL;
+}
+static BOOL check_yuv420(BYTE** planes, UINT32 width, UINT32 height, UINT32 padding)
+{
+ const size_t size = width * height;
+ const size_t uvwidth = (width + 1) / 2;
+ const size_t uvsize = (height + 1) / 2 * uvwidth;
+ const BOOL yOk = check_padding(planes[0], size, padding, "Y");
+ const BOOL uOk = check_padding(planes[1], uvsize, padding, "U");
+ const BOOL vOk = check_padding(planes[2], uvsize, padding, "V");
+ return (yOk && uOk && vOk);
+}
+
+static BOOL check_for_mismatches(const BYTE* planeA, const BYTE* planeB, UINT32 size)
+{
+ BOOL rc = FALSE;
+
+ for (UINT32 x = 0; x < size; x++)
+ {
+ const BYTE a = planeA[x];
+ const BYTE b = planeB[x];
+
+ if (fabsf((float)a - (float)b) > 2.0f)
+ {
+ rc = TRUE;
+ fprintf(stderr, "[%08x] %02x != %02x\n", x, a, b);
+ }
+ }
+
+ return rc;
+}
+
+static BOOL compare_yuv420(BYTE** planesA, BYTE** planesB, UINT32 width, UINT32 height,
+ UINT32 padding)
+{
+ BOOL rc = TRUE;
+ const size_t size = width * height;
+ const size_t uvwidth = (width + 1) / 2;
+ const size_t uvsize = (height + 1) / 2 * uvwidth;
+
+ if (check_for_mismatches(planesA[0], planesB[0], size))
+ {
+ fprintf(stderr, "Mismatch in Y planes!");
+ rc = FALSE;
+ }
+
+ if (check_for_mismatches(planesA[1], planesB[1], uvsize))
+ {
+ fprintf(stderr, "Mismatch in U planes!");
+ rc = FALSE;
+ }
+
+ if (check_for_mismatches(planesA[2], planesB[2], uvsize))
+ {
+ fprintf(stderr, "Mismatch in V planes!");
+ rc = FALSE;
+ }
+
+ return rc;
+}
+
+static BOOL TestPrimitiveRgbToLumaChroma(primitives_t* prims, prim_size_t roi, UINT32 version)
+{
+ BOOL res = FALSE;
+ UINT32 awidth = 0;
+ UINT32 aheight = 0;
+ BYTE* luma[3] = { 0 };
+ BYTE* chroma[3] = { 0 };
+ BYTE* lumaGeneric[3] = { 0 };
+ BYTE* chromaGeneric[3] = { 0 };
+ UINT32 yuv_step[3];
+ BYTE* rgb = NULL;
+ size_t size = 0;
+ size_t uvwidth = 0;
+ const size_t padding = 0x1000;
+ UINT32 stride = 0;
+ __RGBToAVC444YUV_t fkt = NULL;
+ __RGBToAVC444YUV_t gen = NULL;
+ const UINT32 formats[] = { PIXEL_FORMAT_XRGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_ARGB32,
+ PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_RGBX32,
+ PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_BGRX32 };
+ PROFILER_DEFINE(rgbToYUV444)
+ PROFILER_DEFINE(rgbToYUV444opt)
+ /* Buffers need to be 16x16 aligned. */
+ awidth = roi.width;
+
+ if (awidth % 16 != 0)
+ awidth += 16 - roi.width % 16;
+
+ aheight = roi.height;
+
+ if (aheight % 16 != 0)
+ aheight += 16 - roi.height % 16;
+
+ stride = awidth * sizeof(UINT32);
+ size = awidth * aheight;
+ uvwidth = (awidth + 1) / 2;
+
+ if (!prims || !generic)
+ return FALSE;
+
+ switch (version)
+ {
+ case 1:
+ fkt = prims->RGBToAVC444YUV;
+ gen = generic->RGBToAVC444YUV;
+ break;
+
+ case 2:
+ fkt = prims->RGBToAVC444YUVv2;
+ gen = generic->RGBToAVC444YUVv2;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (!fkt || !gen)
+ return FALSE;
+
+ fprintf(stderr, "Running AVC444 on frame size %" PRIu32 "x%" PRIu32 "\n", roi.width,
+ roi.height);
+
+ /* Test RGB to YUV444 conversion and vice versa */
+ if (!(rgb = set_padding(size * sizeof(UINT32), padding)))
+ goto fail;
+
+ if (!allocate_yuv420(luma, awidth, aheight, padding))
+ goto fail;
+
+ if (!allocate_yuv420(chroma, awidth, aheight, padding))
+ goto fail;
+
+ if (!allocate_yuv420(lumaGeneric, awidth, aheight, padding))
+ goto fail;
+
+ if (!allocate_yuv420(chromaGeneric, awidth, aheight, padding))
+ goto fail;
+
+ for (UINT32 y = 0; y < roi.height; y++)
+ {
+ BYTE* line = &rgb[y * stride];
+
+ for (UINT32 x = 0; x < roi.width; x++)
+ {
+#if 1
+ line[x * 4 + 0] = rand();
+ line[x * 4 + 1] = rand();
+ line[x * 4 + 2] = rand();
+ line[x * 4 + 3] = rand();
+#else
+ line[x * 4 + 0] = (y * roi.width + x) * 16 + 5;
+ line[x * 4 + 1] = (y * roi.width + x) * 16 + 7;
+ line[x * 4 + 2] = (y * roi.width + x) * 16 + 11;
+ line[x * 4 + 3] = (y * roi.width + x) * 16 + 0;
+#endif
+ }
+ }
+
+ yuv_step[0] = awidth;
+ yuv_step[1] = uvwidth;
+ yuv_step[2] = uvwidth;
+
+ for (UINT32 x = 0; x < sizeof(formats) / sizeof(formats[0]); x++)
+ {
+ pstatus_t rc = -1;
+ const UINT32 DstFormat = formats[x];
+ printf("Testing destination color format %s\n", FreeRDPGetColorFormatName(DstFormat));
+ PROFILER_CREATE(rgbToYUV444, "RGBToYUV444-generic")
+ PROFILER_CREATE(rgbToYUV444opt, "RGBToYUV444-optimized")
+
+ for (UINT32 cnt = 0; cnt < 10; cnt++)
+ {
+ PROFILER_ENTER(rgbToYUV444opt)
+ rc = fkt(rgb, DstFormat, stride, luma, yuv_step, chroma, yuv_step, &roi);
+ PROFILER_EXIT(rgbToYUV444opt)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto loop_fail;
+ }
+
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(rgbToYUV444opt)
+ PROFILER_PRINT_FOOTER
+
+ if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ if (!check_yuv420(luma, awidth, aheight, padding) ||
+ !check_yuv420(chroma, awidth, aheight, padding))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ for (UINT32 cnt = 0; cnt < 10; cnt++)
+ {
+ PROFILER_ENTER(rgbToYUV444)
+ rc = gen(rgb, DstFormat, stride, lumaGeneric, yuv_step, chromaGeneric, yuv_step, &roi);
+ PROFILER_EXIT(rgbToYUV444)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto loop_fail;
+ }
+
+ PROFILER_PRINT_HEADER
+ PROFILER_PRINT(rgbToYUV444)
+ PROFILER_PRINT_FOOTER
+
+ if (!check_padding(rgb, size * sizeof(UINT32), padding, "rgb"))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ if (!check_yuv420(lumaGeneric, awidth, aheight, padding) ||
+ !check_yuv420(chromaGeneric, awidth, aheight, padding))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ if (!compare_yuv420(luma, lumaGeneric, awidth, aheight, padding) ||
+ !compare_yuv420(chroma, chromaGeneric, awidth, aheight, padding))
+ {
+ rc = -1;
+ goto loop_fail;
+ }
+
+ loop_fail:
+ PROFILER_FREE(rgbToYUV444)
+ PROFILER_FREE(rgbToYUV444opt)
+
+ if (rc != PRIMITIVES_SUCCESS)
+ goto fail;
+ }
+
+ res = TRUE;
+fail:
+ free_padding(rgb, padding);
+ free_yuv420(luma, padding);
+ free_yuv420(chroma, padding);
+ free_yuv420(lumaGeneric, padding);
+ free_yuv420(chromaGeneric, padding);
+ return res;
+}
+
+int TestPrimitivesYUV(int argc, char* argv[])
+{
+ BOOL large = (argc > 1);
+ int rc = -1;
+ WINPR_UNUSED(argc);
+ WINPR_UNUSED(argv);
+ prim_test_setup(FALSE);
+ primitives_t* prims = primitives_get();
+
+ for (UINT32 x = 0; x < 5; x++)
+ {
+ prim_size_t roi;
+
+ if (argc > 1)
+ {
+ int crc = sscanf(argv[1], "%" PRIu32 "x%" PRIu32, &roi.width, &roi.height);
+
+ if (crc != 2)
+ {
+ roi.width = 1920;
+ roi.height = 1080;
+ }
+ }
+ else
+ get_size(large, &roi.width, &roi.height);
+
+ printf("-------------------- GENERIC ------------------------\n");
+
+ if (!TestPrimitiveYUV(generic, roi, TRUE))
+ {
+ printf("TestPrimitiveYUV (444) failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("------------------- OPTIMIZED -----------------------\n");
+
+ if (!TestPrimitiveYUV(prims, roi, TRUE))
+ {
+ printf("TestPrimitiveYUV (444) failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("-------------------- GENERIC ------------------------\n");
+
+ if (!TestPrimitiveYUV(generic, roi, FALSE))
+ {
+ printf("TestPrimitiveYUV (420) failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("------------------- OPTIMIZED -----------------------\n");
+
+ if (!TestPrimitiveYUV(prims, roi, FALSE))
+ {
+ printf("TestPrimitiveYUV (420) failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("-------------------- GENERIC ------------------------\n");
+
+ if (!TestPrimitiveYUVCombine(generic, roi))
+ {
+ printf("TestPrimitiveYUVCombine failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("------------------- OPTIMIZED -----------------------\n");
+
+ if (!TestPrimitiveYUVCombine(prims, roi))
+ {
+ printf("TestPrimitiveYUVCombine failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("------------------- OPTIMIZED -----------------------\n");
+
+ if (!TestPrimitiveRgbToLumaChroma(prims, roi, 1))
+ {
+ printf("TestPrimitiveRgbToLumaChroma failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ printf("-------------------- GENERIC ------------------------\n");
+
+ if (!TestPrimitiveRgbToLumaChroma(prims, roi, 2))
+ {
+ printf("TestPrimitiveYUVCombine failed.\n");
+ goto end;
+ }
+
+ printf("---------------------- END --------------------------\n");
+ }
+
+ rc = 0;
+end:
+ return rc;
+}