mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-14 00:14:11 +09:00
[winpr,image] use fuzzy compare
* Add winpr_image_equal_ex to allow comparison of lossy compressed formats, ignoring color depth and alpha * Adjust tests to utilize winpr_image_equal_ex
This commit is contained in:
@@ -78,6 +78,14 @@ typedef struct
|
||||
UINT32 bytesPerPixel;
|
||||
} wImage;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WINPR_IMAGE_CMP_NO_FLAGS = 0,
|
||||
WINPR_IMAGE_CMP_IGNORE_DEPTH = 1,
|
||||
WINPR_IMAGE_CMP_IGNORE_ALPHA = 2,
|
||||
WINPR_IMAGE_CMP_FUZZY = 4
|
||||
} wImageFlags;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@@ -104,7 +112,7 @@ extern "C"
|
||||
WINPR_API BOOL winpr_image_format_is_supported(UINT32 format);
|
||||
WINPR_API const char* winpr_image_format_extension(UINT32 format);
|
||||
WINPR_API const char* winpr_image_format_mime(UINT32 format);
|
||||
WINPR_API BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB);
|
||||
WINPR_API BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB, UINT32 flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -925,37 +925,131 @@ BOOL winpr_image_format_is_supported(UINT32 format)
|
||||
}
|
||||
}
|
||||
|
||||
BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB)
|
||||
static BYTE* convert(const wImage* image, size_t* pstride, UINT32 flags)
|
||||
{
|
||||
WINPR_ASSERT(image);
|
||||
WINPR_ASSERT(pstride);
|
||||
|
||||
*pstride = 0;
|
||||
if (image->bitsPerPixel < 24)
|
||||
return NULL;
|
||||
|
||||
const size_t stride = image->width * 4ull;
|
||||
BYTE* data = calloc(stride, image->height);
|
||||
if (data)
|
||||
{
|
||||
for (size_t y = 0; y < image->height; y++)
|
||||
{
|
||||
const BYTE* srcLine = &image->data[image->scanline * y];
|
||||
BYTE* dstLine = &data[stride * y];
|
||||
if (image->bitsPerPixel == 32)
|
||||
memcpy(dstLine, srcLine, stride);
|
||||
else
|
||||
{
|
||||
for (size_t x = 0; x < image->width; x++)
|
||||
{
|
||||
const BYTE* src = &srcLine[image->bytesPerPixel * x];
|
||||
BYTE* dst = &dstLine[4ull * x];
|
||||
BYTE b = *src++;
|
||||
BYTE g = *src++;
|
||||
BYTE r = *src++;
|
||||
|
||||
*dst++ = b;
|
||||
*dst++ = g;
|
||||
*dst++ = r;
|
||||
*dst++ = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
*pstride = stride;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
|
||||
{
|
||||
if (a != b)
|
||||
{
|
||||
if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
|
||||
{
|
||||
const int diff = abs((int)a) - abs((int)b);
|
||||
/* filter out quantization errors */
|
||||
if (diff > 6)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL compare_pixel(const BYTE* pa, const BYTE* pb, UINT32 flags)
|
||||
{
|
||||
WINPR_ASSERT(pa);
|
||||
WINPR_ASSERT(pb);
|
||||
|
||||
if (!compare_byte_relaxed(*pa++, *pb++, flags))
|
||||
return FALSE;
|
||||
if (!compare_byte_relaxed(*pa++, *pb++, flags))
|
||||
return FALSE;
|
||||
if (!compare_byte_relaxed(*pa++, *pb++, flags))
|
||||
return FALSE;
|
||||
if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
|
||||
{
|
||||
if (!compare_byte_relaxed(*pa++, *pb++, flags))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB, UINT32 flags)
|
||||
{
|
||||
if (imageA == imageB)
|
||||
return TRUE;
|
||||
if (!imageA || !imageB)
|
||||
return FALSE;
|
||||
|
||||
if (imageA->bitsPerPixel != imageB->bitsPerPixel)
|
||||
return FALSE;
|
||||
if (imageA->bytesPerPixel != imageB->bytesPerPixel)
|
||||
return FALSE;
|
||||
if (imageA->height != imageB->height)
|
||||
return FALSE;
|
||||
if (imageA->width != imageB->width)
|
||||
return FALSE;
|
||||
if (imageA->scanline != imageB->scanline)
|
||||
return FALSE;
|
||||
|
||||
const size_t sizeA = 1ull * imageA->scanline * imageA->height;
|
||||
for (size_t x = 0; x < sizeA; x++)
|
||||
if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
|
||||
{
|
||||
const BYTE a = imageA->data[x];
|
||||
const BYTE b = imageB->data[x];
|
||||
if (a != b)
|
||||
if (imageA->bitsPerPixel != imageB->bitsPerPixel)
|
||||
return FALSE;
|
||||
if (imageA->bytesPerPixel != imageB->bytesPerPixel)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL rc = FALSE;
|
||||
size_t astride = 0;
|
||||
size_t bstride = 0;
|
||||
BYTE* dataA = convert(imageA, &astride, flags);
|
||||
BYTE* dataB = convert(imageA, &bstride, flags);
|
||||
if (dataA && dataB && (astride == bstride))
|
||||
{
|
||||
rc = TRUE;
|
||||
for (size_t y = 0; y < imageA->height; y++)
|
||||
{
|
||||
/* filter out quantization errors */
|
||||
if (abs((int)a - (int)b) > 6)
|
||||
return FALSE;
|
||||
const BYTE* lineA = &dataA[astride * y];
|
||||
const BYTE* lineB = &dataB[bstride * y];
|
||||
|
||||
for (size_t x = 0; x < imageA->width; x++)
|
||||
{
|
||||
const BYTE* pa = &lineA[x * 4ull];
|
||||
const BYTE* pb = &lineB[x * 4ull];
|
||||
|
||||
if (!compare_pixel(pa, pb, flags))
|
||||
rc = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
free(dataA);
|
||||
free(dataB);
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char* winpr_image_format_mime(UINT32 format)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/image.h>
|
||||
@@ -7,6 +8,13 @@
|
||||
static const char test_src_filename[] = TEST_SOURCE_PATH "/rgb";
|
||||
static const char test_bin_filename[] = TEST_BINARY_PATH "/rgb";
|
||||
|
||||
static BOOL test_image_equal(const wImage* imageA, const wImage* imageB)
|
||||
{
|
||||
return winpr_image_equal(imageA, imageB,
|
||||
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
|
||||
WINPR_IMAGE_CMP_FUZZY);
|
||||
}
|
||||
|
||||
static BOOL test_equal_to(const wImage* bmp, const char* name, UINT32 format)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
@@ -23,7 +31,7 @@ static BOOL test_equal_to(const wImage* bmp, const char* name, UINT32 format)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = winpr_image_equal(bmp, cmp);
|
||||
rc = test_image_equal(bmp, cmp);
|
||||
if (!rc)
|
||||
fprintf(stderr, "[%s] winpr_image_eqal failed", __func__);
|
||||
|
||||
@@ -134,13 +142,17 @@ static BOOL test_read_write_compare(const char* tname, const char* tdst, UINT32
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!winpr_image_equal(bmp1, bmp2))
|
||||
if (!winpr_image_equal(bmp1, bmp2,
|
||||
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
|
||||
WINPR_IMAGE_CMP_FUZZY))
|
||||
{
|
||||
fprintf(stderr, "[%s] winpr_image_eqal failed bmp1 bmp2", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = winpr_image_equal(bmp3, bmp2);
|
||||
rc = winpr_image_equal(bmp3, bmp2,
|
||||
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
|
||||
WINPR_IMAGE_CMP_FUZZY);
|
||||
if (!rc)
|
||||
fprintf(stderr, "[%s] winpr_image_eqal failed bmp3 bmp2", __func__);
|
||||
fail:
|
||||
|
||||
Reference in New Issue
Block a user