#include #include #include #include #include #include #include #include #include #include #include #include #include "TestFreeRDPHelpers.h" // #define CREATE_TEST_OUTPUT static bool run_encode_decode_single(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder, BITMAP_INTERLEAVED_CONTEXT* decoder #if defined(WITH_PROFILER) , PROFILER* profiler_comp, PROFILER* profiler_decomp #endif ) { bool rc2 = false; bool rc = 0; const UINT32 w = 64; const UINT32 h = 64; const UINT32 x = 0; const UINT32 y = 0; const UINT32 format = PIXEL_FORMAT_RGBX32; const UINT32 bstep = FreeRDPGetBytesPerPixel(format); const size_t step = (13ULL + w) * 4ULL; const size_t SrcSize = step * h; const int maxDiff = 4 * ((bpp < 24) ? 2 : 1); UINT32 DstSize = SrcSize; BYTE* pSrcData = calloc(1, SrcSize); BYTE* pDstData = calloc(1, SrcSize); BYTE* tmp = calloc(1, SrcSize); if (!pSrcData || !pDstData || !tmp) goto fail; if (winpr_RAND(pSrcData, SrcSize) < 0) goto fail; if (!bitmap_interleaved_context_reset(encoder) || !bitmap_interleaved_context_reset(decoder)) goto fail; PROFILER_ENTER(profiler_comp) rc = interleaved_compress(encoder, tmp, &DstSize, w, h, pSrcData, format, step, x, y, nullptr, bpp); PROFILER_EXIT(profiler_comp) if (!rc) goto fail; PROFILER_ENTER(profiler_decomp) rc = interleaved_decompress(decoder, tmp, DstSize, w, h, bpp, pDstData, format, step, x, y, w, h, nullptr); PROFILER_EXIT(profiler_decomp) if (!rc) goto fail; for (UINT32 i = 0; i < h; i++) { const BYTE* srcLine = &pSrcData[i * step]; const BYTE* dstLine = &pDstData[i * step]; for (UINT32 j = 0; j < w; j++) { BYTE r = 0; BYTE g = 0; BYTE b = 0; BYTE dr = 0; BYTE dg = 0; BYTE db = 0; const UINT32 srcColor = FreeRDPReadColor(&srcLine[1ULL * j * bstep], format); const UINT32 dstColor = FreeRDPReadColor(&dstLine[1ULL * j * bstep], format); FreeRDPSplitColor(srcColor, format, &r, &g, &b, nullptr, nullptr); FreeRDPSplitColor(dstColor, format, &dr, &dg, &db, nullptr, nullptr); if (abs(r - dr) > maxDiff) goto fail; if (abs(g - dg) > maxDiff) goto fail; if (abs(b - db) > maxDiff) goto fail; } } rc2 = true; fail: free(pSrcData); free(pDstData); free(tmp); return rc2; } static const char* get_profiler_name(bool encode, UINT16 bpp) { switch (bpp) { case 24: if (encode) return "interleaved_compress 24bpp"; else return "interleaved_decompress 24bpp"; case 16: if (encode) return "interleaved_compress 16bpp"; else return "interleaved_decompress 16bpp"; case 15: if (encode) return "interleaved_compress 15bpp"; else return "interleaved_decompress 15bpp"; default: return "configuration error!"; } } static bool run_encode_decode(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder, BITMAP_INTERLEAVED_CONTEXT* decoder) { bool rc = false; PROFILER_DEFINE(profiler_comp) PROFILER_DEFINE(profiler_decomp) PROFILER_CREATE(profiler_comp, get_profiler_name(true, bpp)) PROFILER_CREATE(profiler_decomp, get_profiler_name(false, bpp)) for (UINT32 x = 0; x < 50; x++) { if (!run_encode_decode_single(bpp, encoder, decoder #if defined(WITH_PROFILER) , profiler_comp, profiler_decomp #endif )) goto fail; } rc = true; fail: PROFILER_PRINT_HEADER PROFILER_PRINT(profiler_comp) PROFILER_PRINT(profiler_decomp) PROFILER_PRINT_FOOTER PROFILER_FREE(profiler_comp) PROFILER_FREE(profiler_decomp) return rc; } static bool TestColorConversion(void) { const UINT32 formats[] = { PIXEL_FORMAT_RGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_RGB16 }; /* Check color conversion 15/16 -> 32bit maps to proper values */ for (UINT32 x = 0; x < ARRAYSIZE(formats); x++) { const UINT32 dstFormat = PIXEL_FORMAT_RGBA32; const UINT32 format = formats[x]; const UINT32 colorLow = FreeRDPGetColor(format, 0, 0, 0, 255); const UINT32 colorHigh = FreeRDPGetColor(format, 255, 255, 255, 255); const UINT32 colorLow32 = FreeRDPConvertColor(colorLow, format, dstFormat, nullptr); const UINT32 colorHigh32 = FreeRDPConvertColor(colorHigh, format, dstFormat, nullptr); BYTE r = 0; BYTE g = 0; BYTE b = 0; BYTE a = 0; FreeRDPSplitColor(colorLow32, dstFormat, &r, &g, &b, &a, nullptr); if ((r != 0) || (g != 0) || (b != 0)) return false; FreeRDPSplitColor(colorHigh32, dstFormat, &r, &g, &b, &a, nullptr); if ((r != 255) || (g != 255) || (b != 255)) return false; } return true; } static bool RunEncoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height, uint32_t step, uint32_t bpp) { bool rc = false; void* data = nullptr; void* encdata = nullptr; BITMAP_INTERLEAVED_CONTEXT* encoder = bitmap_interleaved_context_new(true); if (!encoder) goto fail; size_t srclen = 0; data = test_codec_helper_read_data("interleaved", "bmp", name, &srclen); if (!data) goto fail; encdata = calloc(srclen, 1); if (!encdata) goto fail; for (size_t x = 0; x < 42; x++) { uint32_t enclen = WINPR_ASSERTING_INT_CAST(uint32_t, srclen); if (!interleaved_compress(encoder, encdata, &enclen, width, height, data, format, step, 0, 0, nullptr, bpp)) goto fail; char encname[128] = WINPR_C_ARRAY_INIT; (void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp); #if defined(CREATE_TEST_OUTPUT) test_codec_helper_write_data("interleaved", encname, name, encdata, enclen); #else if (!test_codec_helper_compare("interleaved", encname, name, encdata, enclen)) goto fail; #endif } rc = true; fail: free(data); free(encdata); bitmap_interleaved_context_free(encoder); return rc; } static bool RunDecoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height, uint32_t step, uint32_t bpp) { bool rc = false; void* data = nullptr; void* decdata = nullptr; BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(false); if (!decoder) goto fail; char encname[128] = WINPR_C_ARRAY_INIT; (void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp); size_t srclen = 0; data = test_codec_helper_read_data("interleaved", encname, name, &srclen); if (!data) goto fail; const size_t declen = 1ULL * step * height; decdata = calloc(step, height); if (!decdata) goto fail; for (size_t x = 0; x < 42; x++) { if (!interleaved_decompress(decoder, data, WINPR_ASSERTING_INT_CAST(uint32_t, srclen), width, height, bpp, decdata, format, step, 0, 0, width, height, nullptr)) goto fail; char decname[128] = WINPR_C_ARRAY_INIT; (void)_snprintf(decname, sizeof(decname), "dec-%s", encname); #if defined(CREATE_TEST_OUTPUT) test_codec_helper_write_data("interleaved", decname, name, decdata, declen); #else if (!test_codec_helper_compare("interleaved", decname, name, decdata, declen)) goto fail; #endif } rc = true; fail: free(data); free(decdata); bitmap_interleaved_context_free(decoder); return rc; } /* The encoder expects a JSON that describes a test cast: * * [ * { * "name": "somestring", * "format": "somestring", * "width": , * "height": , * "step": , * "bpp": * }, * {...}, * ... * ] */ static bool isObjectValid(const WINPR_JSON* obj) { if (!obj || !WINPR_JSON_IsObject(obj)) return false; const char* strvalues[] = { "name", "format" }; for (size_t x = 0; x < ARRAYSIZE(strvalues); x++) { const char* val = strvalues[x]; if (!WINPR_JSON_HasObjectItem(obj, val)) return false; WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val); if (!jval) return false; if (!WINPR_JSON_IsString(jval)) return false; } const char* values[] = { "width", "height", "step" }; for (size_t x = 0; x < ARRAYSIZE(values); x++) { const char* val = values[x]; if (!WINPR_JSON_HasObjectItem(obj, val)) return false; WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val); if (!jval) return false; if (!WINPR_JSON_IsNumber(jval)) return false; const double dval = WINPR_JSON_GetNumberValue(jval); if (dval <= 0.0) return false; } { const char* val = "bpp"; if (!WINPR_JSON_HasObjectItem(obj, val)) return false; WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val); if (!jval) return false; if (!WINPR_JSON_IsArray(jval)) return false; for (size_t x = 0; x < WINPR_JSON_GetArraySize(jval); x++) { WINPR_JSON* aval = WINPR_JSON_GetArrayItem(jval, x); if (!jval || !WINPR_JSON_IsNumber(aval)) return false; } } return true; } static bool TestEncoder(void) { bool rc = false; WINPR_JSON* json = nullptr; char* file = nullptr; char* path = GetCombinedPath(CMAKE_CURRENT_SOURCE_DIR, "interleaved"); if (!path) goto fail; file = GetCombinedPath(path, "encoder.json"); if (!file) goto fail; json = WINPR_JSON_ParseFromFile(file); if (!json) goto fail; if (!WINPR_JSON_IsArray(json)) goto fail; for (size_t x = 0; x < WINPR_JSON_GetArraySize(json); x++) { WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x); if (!isObjectValid(obj)) goto fail; const char* name = WINPR_JSON_GetStringValue(WINPR_JSON_GetObjectItem(obj, "name")); const uint32_t format = WINPR_ASSERTING_INT_CAST( uint32_t, FreeRDPGetColorFromatFromName( WINPR_JSON_GetStringValue(WINPR_JSON_GetObjectItem(obj, "format")))); const uint32_t width = WINPR_ASSERTING_INT_CAST( uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "width"))); const uint32_t height = WINPR_ASSERTING_INT_CAST( uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "height"))); const uint32_t step = WINPR_ASSERTING_INT_CAST( uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "step"))); WINPR_JSON* jbpp = WINPR_JSON_GetObjectItem(obj, "bpp"); for (size_t x = 0; x < WINPR_JSON_GetArraySize(jbpp); x++) { const uint32_t bpp = WINPR_ASSERTING_INT_CAST( uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetArrayItem(jbpp, x))); if (!RunEncoderTest(name, format, width, height, step, bpp)) goto fail; if (!RunDecoderTest(name, format, width, height, step, bpp)) goto fail; } } rc = true; fail: WINPR_JSON_Delete(json); free(path); free(file); return rc; } int TestFreeRDPCodecInterleaved(int argc, char* argv[]) { BITMAP_INTERLEAVED_CONTEXT* encoder = nullptr; BITMAP_INTERLEAVED_CONTEXT* decoder = nullptr; int rc = -1; WINPR_UNUSED(argc); WINPR_UNUSED(argv); encoder = bitmap_interleaved_context_new(true); decoder = bitmap_interleaved_context_new(false); if (!encoder || !decoder) goto fail; if (!run_encode_decode(24, encoder, decoder)) goto fail; if (!run_encode_decode(16, encoder, decoder)) goto fail; if (!run_encode_decode(15, encoder, decoder)) goto fail; if (!TestColorConversion()) goto fail; if (!TestEncoder()) goto fail; rc = 0; fail: bitmap_interleaved_context_free(encoder); bitmap_interleaved_context_free(decoder); return rc; }