#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/path.h>
#include <winpr/image.h>
#include <winpr/print.h>
#include <winpr/wlog.h>
#include <winpr/sysinfo.h>
#include <winpr/file.h>

#include <freerdp/codec/region.h>

#include <freerdp/codec/progressive.h>

#include "../progressive.h"

/**
 * Microsoft Progressive Codec Sample Data
 * (available under NDA only)
 *
 * <enc/dec>_<image#>_<quarter#>_<prog%>_<bitmap>.<type>
 *
 * readme.pdf
 *
 * bitmaps/
 * 	1920by1080-SampleImage1.bmp
 * 	1920by1080-SampleImage2.bmp
 * 	1920by1080-SampleImage3.bmp
 *
 * compress/
 * 	enc_0_0_025_sampleimage1.bin
 * 	enc_0_0_050_sampleimage1.bin
 * 	enc_0_0_075_sampleimage1.bin
 * 	enc_0_0_100_sampleimage1.bin
 * 	enc_0_1_025_sampleimage1.bin
 * 	enc_0_1_050_sampleimage1.bin
 * 	enc_0_1_075_sampleimage1.bin
 * 	enc_0_1_100_sampleimage1.bin
 * 	enc_0_2_025_sampleimage1.bin
 * 	enc_0_2_050_sampleimage1.bin
 * 	enc_0_2_075_sampleimage1.bin
 * 	enc_0_2_100_sampleimage1.bin
 * 	enc_0_3_025_sampleimage1.bin
 * 	enc_0_3_050_sampleimage1.bin
 * 	enc_0_3_075_sampleimage1.bin
 * 	enc_0_3_100_sampleimage1.bin
 * 	enc_1_0_025_sampleimage2.bin
 * 	enc_1_0_050_sampleimage2.bin
 * 	enc_1_0_075_sampleimage2.bin
 * 	enc_1_0_100_sampleimage2.bin
 * 	enc_1_1_025_sampleimage2.bin
 * 	enc_1_1_050_sampleimage2.bin
 * 	enc_1_1_075_sampleimage2.bin
 * 	enc_1_1_100_sampleimage2.bin
 * 	enc_1_2_025_sampleimage2.bin
 * 	enc_1_2_050_sampleimage2.bin
 * 	enc_1_2_075_sampleimage2.bin
 * 	enc_1_2_100_sampleimage2.bin
 * 	enc_1_3_025_sampleimage2.bin
 * 	enc_1_3_050_sampleimage2.bin
 * 	enc_1_3_075_sampleimage2.bin
 * 	enc_1_3_100_sampleimage2.bin
 * 	enc_2_0_025_sampleimage3.bin
 * 	enc_2_0_050_sampleimage3.bin
 * 	enc_2_0_075_sampleimage3.bin
 * 	enc_2_0_100_sampleimage3.bin
 * 	enc_2_1_025_sampleimage3.bin
 * 	enc_2_1_050_sampleimage3.bin
 * 	enc_2_1_075_sampleimage3.bin
 * 	enc_2_1_100_sampleimage3.bin
 * 	enc_2_2_025_sampleimage3.bin
 * 	enc_2_2_050_sampleimage3.bin
 * 	enc_2_2_075_sampleimage3.bin
 * 	enc_2_2_100_sampleimage3.bin
 * 	enc_2_3_025_sampleimage3.bin
 * 	enc_2_3_050_sampleimage3.bin
 * 	enc_2_3_075_sampleimage3.bin
 * 	enc_2_3_100_sampleimage3.bin
 *
 * decompress/
 * 	dec_0_0_025_sampleimage1.bmp
 * 	dec_0_0_050_sampleimage1.bmp
 * 	dec_0_0_075_sampleimage1.bmp
 * 	dec_0_0_100_sampleimage1.bmp
 * 	dec_0_1_025_sampleimage1.bmp
 * 	dec_0_1_050_sampleimage1.bmp
 * 	dec_0_1_075_sampleimage1.bmp
 * 	dec_0_1_100_sampleimage1.bmp
 * 	dec_0_2_025_sampleimage1.bmp
 * 	dec_0_2_050_sampleimage1.bmp
 * 	dec_0_2_075_sampleimage1.bmp
 * 	dec_0_2_100_sampleimage1.bmp
 * 	dec_0_3_025_sampleimage1.bmp
 * 	dec_0_3_050_sampleimage1.bmp
 * 	dec_0_3_075_sampleimage1.bmp
 * 	dec_0_3_100_sampleimage1.bmp
 * 	dec_1_0_025_sampleimage2.bmp
 * 	dec_1_0_050_sampleimage2.bmp
 * 	dec_1_0_075_sampleimage2.bmp
 * 	dec_1_0_100_sampleimage2.bmp
 * 	dec_1_1_025_sampleimage2.bmp
 * 	dec_1_1_050_sampleimage2.bmp
 * 	dec_1_1_075_sampleimage2.bmp
 * 	dec_1_1_100_sampleimage2.bmp
 * 	dec_1_2_025_sampleimage2.bmp
 * 	dec_1_2_050_sampleimage2.bmp
 * 	dec_1_2_075_sampleimage2.bmp
 * 	dec_1_2_100_sampleimage2.bmp
 * 	dec_1_3_025_sampleimage2.bmp
 * 	dec_1_3_050_sampleimage2.bmp
 * 	dec_1_3_075_sampleimage2.bmp
 * 	dec_1_3_100_sampleimage2.bmp
 * 	dec_2_0_025_sampleimage3.bmp
 * 	dec_2_0_050_sampleimage3.bmp
 * 	dec_2_0_075_sampleimage3.bmp
 * 	dec_2_0_100_sampleimage3.bmp
 * 	dec_2_1_025_sampleimage3.bmp
 * 	dec_2_1_050_sampleimage3.bmp
 * 	dec_2_1_075_sampleimage3.bmp
 * 	dec_2_1_100_sampleimage3.bmp
 * 	dec_2_2_025_sampleimage3.bmp
 * 	dec_2_2_050_sampleimage3.bmp
 * 	dec_2_2_075_sampleimage3.bmp
 * 	dec_2_2_100_sampleimage3.bmp
 * 	dec_2_3_025_sampleimage3.bmp
 * 	dec_2_3_050_sampleimage3.bmp
 * 	dec_2_3_075_sampleimage3.bmp
 * 	dec_2_3_100_sampleimage3.bmp
 */

typedef struct
{
	BYTE* buffer;
	size_t size;
} EGFX_SAMPLE_FILE;

static int g_Width = 0;
static int g_Height = 0;
static int g_DstStep = 0;
static BYTE* g_DstData = NULL;

static void sample_file_free(EGFX_SAMPLE_FILE* file)
{
	if (!file)
		return;

	free(file->buffer);
	file->buffer = NULL;
	file->size = 0;
}

static void test_fill_image_alpha_channel(BYTE* data, int width, int height, BYTE value)
{
	UINT32* pixel = NULL;

	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			pixel = (UINT32*)&data[((i * width) + j) * 4];
			*pixel = ((*pixel & 0x00FFFFFF) | (value << 24));
		}
	}
}

static void* test_image_memset32(UINT32* ptr, UINT32 fill, size_t length)
{
	while (length--)
	{
		*ptr++ = fill;
	}

	return (void*)ptr;
}

static int test_image_fill(BYTE* pDstData, int nDstStep, int nXDst, int nYDst, int nWidth,
                           int nHeight, UINT32 color)
{
	UINT32* pDstPixel = NULL;

	if (nDstStep < 0)
		nDstStep = 4 * nWidth;

	for (int y = 0; y < nHeight; y++)
	{
		pDstPixel = (UINT32*)&pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
		test_image_memset32(pDstPixel, color, nWidth);
	}

	return 1;
}

static int test_image_fill_quarter(BYTE* pDstData, int nDstStep, int nWidth, int nHeight,
                                   UINT32 color, int quarter)
{
	int x = 0;
	int y = 0;
	int width = 0;
	int height = 0;

	switch (quarter)
	{
		case 0:
			x = 0;
			y = 0;
			width = nWidth / 2;
			height = nHeight / 2;
			break;

		case 1:
			x = nWidth / 2;
			y = nHeight / 2;
			width = nWidth / 2;
			height = nHeight / 2;
			break;

		case 2:
			x = 0;
			y = nHeight / 2;
			width = nWidth / 2;
			height = nHeight / 2;
			break;

		case 3:
			x = nWidth / 2;
			y = 0;
			width = nWidth / 2;
			height = nHeight / 2;
			break;
	}

	test_image_fill(pDstData, nDstStep, x, y, width, height, 0xFF000000);
	return 1;
}

static int test_image_fill_unused_quarters(BYTE* pDstData, int nDstStep, int nWidth, int nHeight,
                                           UINT32 color, int quarter)
{
	return 1;

	if (quarter == 0)
	{
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
	}
	else if (quarter == 1)
	{
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
	}
	else if (quarter == 2)
	{
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
	}
	else if (quarter == 3)
	{
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
		test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
	}

	return 1;
}

static BYTE* test_progressive_load_file(const char* path, const char* file, size_t* size)
{
	char* filename = GetCombinedPath(path, file);

	if (!filename)
		return NULL;

	FILE* fp = winpr_fopen(filename, "r");
	free(filename);

	if (!fp)
		return NULL;

	_fseeki64(fp, 0, SEEK_END);
	const INT64 pos = _ftelli64(fp);
	WINPR_ASSERT(pos >= 0);
	WINPR_ASSERT(pos <= SIZE_MAX);
	*size = (size_t)pos;
	_fseeki64(fp, 0, SEEK_SET);
	BYTE* buffer = (BYTE*)malloc(*size);

	if (!buffer)
	{
		fclose(fp);
		return NULL;
	}

	if (fread(buffer, *size, 1, fp) != 1)
	{
		free(buffer);
		fclose(fp);
		return NULL;
	}

	fclose(fp);
	return buffer;
}

static int test_progressive_load_files(char* ms_sample_path, EGFX_SAMPLE_FILE files[3][4][4])
{
	int imageNo = 0;
	int quarterNo = 0;
	int passNo = 0;
	/* image 1 */
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_0_025_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_0_050_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_0_075_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_0_100_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_1_025_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_1_050_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_1_075_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_1_100_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_2_025_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_2_050_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_2_075_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_2_100_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_3_025_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_3_050_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_3_075_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_0_3_100_sampleimage1.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	imageNo++;
	/* image 2 */
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_0_025_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_0_050_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_0_075_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_0_100_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_1_025_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_1_050_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_1_075_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_1_100_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_2_025_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_2_050_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_2_075_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_2_100_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_3_025_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_3_050_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_3_075_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_1_3_100_sampleimage2.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	imageNo++;
	/* image 3 */
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_0_025_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_0_050_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_0_075_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_0_100_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_1_025_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_1_050_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_1_075_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_1_100_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_2_025_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_2_050_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_2_075_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_2_100_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_3_025_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_3_050_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_3_075_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));
	passNo = (passNo + 1) % 4;
	files[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_file(ms_sample_path, "compress/enc_2_3_100_sampleimage3.bin",
	                               &(files[imageNo][quarterNo][passNo].size));

	/* check if all test data has been loaded */

	for (imageNo = 0; imageNo < 3; imageNo++)
	{
		for (quarterNo = 0; quarterNo < 4; quarterNo++)
		{
			for (passNo = 0; passNo < 4; passNo++)
			{
				if (!files[imageNo][quarterNo][passNo].buffer)
					return -1;
			}
		}
	}

	return 1;
}

static BYTE* test_progressive_load_bitmap(char* path, char* file, size_t* size, int quarter)
{
	int status = 0;
	BYTE* buffer = NULL;
	wImage* image = NULL;
	char* filename = NULL;
	filename = GetCombinedPath(path, file);

	if (!filename)
		return NULL;

	image = winpr_image_new();

	if (!image)
		return NULL;

	status = winpr_image_read(image, filename);

	if (status < 0)
		return NULL;

	buffer = image->data;
	*size = image->height * image->scanline;
	test_fill_image_alpha_channel(image->data, image->width, image->height, 0xFF);
	test_image_fill_unused_quarters(image->data, image->scanline, image->width, image->height,
	                                quarter, 0xFF000000);
	winpr_image_free(image, FALSE);
	free(filename);
	return buffer;
}

static int test_progressive_load_bitmaps(char* ms_sample_path, EGFX_SAMPLE_FILE bitmaps[3][4][4])
{
	int imageNo = 0;
	int quarterNo = 0;
	int passNo = 0;
	/* image 1 */
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_025_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_050_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_075_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_100_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_025_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_050_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_075_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_100_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_025_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_050_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_075_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_100_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_025_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_050_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_075_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_100_sampleimage1.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	imageNo++;
	/* image 2 */
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_025_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_050_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_075_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_100_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_025_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_050_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_075_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_100_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_025_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_050_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_075_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_100_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_025_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_050_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_075_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_100_sampleimage2.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	imageNo++;
	/* image 3 */
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_025_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_050_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_075_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_100_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_025_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_050_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_075_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_100_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_025_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_050_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_075_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_100_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	quarterNo = (quarterNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_025_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_050_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_075_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
	passNo = (passNo + 1) % 4;
	bitmaps[imageNo][quarterNo][passNo].buffer =
	    test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_100_sampleimage3.bmp",
	                                 &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);

	/* check if all test data has been loaded */

	for (imageNo = 0; imageNo < 3; imageNo++)
	{
		for (quarterNo = 0; quarterNo < 4; quarterNo++)
		{
			for (passNo = 0; passNo < 4; passNo++)
			{
				if (!bitmaps[imageNo][quarterNo][passNo].buffer)
					return -1;
			}
		}
	}

	return 1;
}

static size_t test_memcmp_count(const BYTE* mem1, const BYTE* mem2, size_t size, int margin)
{
	size_t count = 0;

	for (size_t index = 0; index < size; index++)
	{
		if (*mem1 != *mem2)
		{
			const int error = (*mem1 > *mem2) ? *mem1 - *mem2 : *mem2 - *mem1;

			if (error > margin)
				count++;
		}

		mem1++;
		mem2++;
	}

	return count;
}

static int test_progressive_decode(PROGRESSIVE_CONTEXT* progressive, EGFX_SAMPLE_FILE files[4],
                                   EGFX_SAMPLE_FILE bitmaps[4], int quarter, int count)
{
	int nXSrc = 0;
	int nYSrc = 0;

	RECTANGLE_16 clippingRect = { 0 };
	clippingRect.right = g_Width;
	clippingRect.bottom = g_Height;

	for (int pass = 0; pass < count; pass++)
	{
		const int status =
		    progressive_decompress(progressive, files[pass].buffer, files[pass].size, g_DstData,
		                           PIXEL_FORMAT_XRGB32, g_DstStep, 0, 0, NULL, 0, 0);
		printf("ProgressiveDecompress: status: %d pass: %d\n", status, pass + 1);
		PROGRESSIVE_BLOCK_REGION* region = &(progressive->region);

		switch (quarter)
		{
			case 0:
				clippingRect.left = 0;
				clippingRect.top = 0;
				clippingRect.right = g_Width / 2;
				clippingRect.bottom = g_Height / 2;
				break;

			case 1:
				clippingRect.left = g_Width / 2;
				clippingRect.top = g_Height / 2;
				clippingRect.right = g_Width;
				clippingRect.bottom = g_Height;
				break;

			case 2:
				clippingRect.left = 0;
				clippingRect.top = g_Height / 2;
				clippingRect.right = g_Width / 2;
				clippingRect.bottom = g_Height;
				break;

			case 3:
				clippingRect.left = g_Width / 2;
				clippingRect.top = 0;
				clippingRect.right = g_Width;
				clippingRect.bottom = g_Height / 2;
				break;
		}

		for (UINT16 index = 0; index < region->numTiles; index++)
		{
			RFX_PROGRESSIVE_TILE* tile = region->tiles[index];

			const RECTANGLE_16 tileRect = { tile->x, tile->y, tile->x + tile->width,
				                            tile->y + tile->height };
			RECTANGLE_16 updateRect = { 0 };
			rectangles_intersection(&tileRect, &clippingRect, &updateRect);
			const UINT16 nXDst = updateRect.left;
			const UINT16 nYDst = updateRect.top;
			const UINT16 nWidth = updateRect.right - updateRect.left;
			const UINT16 nHeight = updateRect.bottom - updateRect.top;

			if ((nWidth <= 0) || (nHeight <= 0))
				continue;

			nXSrc = nXDst - tile->x;
			nYSrc = nYDst - tile->y;
			freerdp_image_copy(g_DstData, PIXEL_FORMAT_XRGB32, g_DstStep, nXDst, nYDst, nWidth,
			                   nHeight, tile->data, PIXEL_FORMAT_XRGB32, 64 * 4, nXSrc, nYSrc, NULL,
			                   FREERDP_FLIP_NONE);
		}

		const size_t size = bitmaps[pass].size;
		const size_t cnt = test_memcmp_count(g_DstData, bitmaps[pass].buffer, size, 1);

		if (cnt)
		{
			const float rate = ((float)cnt) / ((float)size) * 100.0f;
			printf("Progressive RemoteFX decompression failure\n");
			printf("Actual, Expected (%" PRIuz "/%" PRIuz " = %.3f%%):\n", cnt, size, rate);
		}

		// WLog_Image(progressive->log, WLOG_TRACE, g_DstData, g_Width, g_Height, 32);
	}

	return 1;
}

static int test_progressive_ms_sample(char* ms_sample_path)
{
	int count = 0;
	int status = 0;
	EGFX_SAMPLE_FILE files[3][4][4] = { 0 };
	EGFX_SAMPLE_FILE bitmaps[3][4][4] = { 0 };
	PROGRESSIVE_CONTEXT* progressive = NULL;
	g_Width = 1920;
	g_Height = 1080;
	g_DstStep = g_Width * 4;
	status = test_progressive_load_files(ms_sample_path, files);

	if (status < 0)
	{
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				for (int k = 0; k < 4; k++)
					sample_file_free(&files[i][j][k]);
			}
		}

		return -1;
	}

	status = test_progressive_load_bitmaps(ms_sample_path, bitmaps);

	if (status < 0)
	{
		for (int i = 0; i < 3; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				for (int k = 0; k < 4; k++)
					sample_file_free(&files[i][j][k]);
			}
		}

		return -1;
	}

	count = 4;
	progressive = progressive_context_new(FALSE);
	g_DstData = winpr_aligned_malloc(g_DstStep * g_Height, 16);
	progressive_create_surface_context(progressive, 0, g_Width, g_Height);

	/* image 1 */

	if (1)
	{
		printf("\nSample Image 1\n");
		test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
		test_progressive_decode(progressive, files[0][0], bitmaps[0][0], 0, count);
		test_progressive_decode(progressive, files[0][1], bitmaps[0][1], 1, count);
		test_progressive_decode(progressive, files[0][2], bitmaps[0][2], 2, count);
		test_progressive_decode(progressive, files[0][3], bitmaps[0][3], 3, count);
	}

	/* image 2 */

	if (0)
	{
		printf("\nSample Image 2\n"); /* sample data is in incorrect order */
		test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
		test_progressive_decode(progressive, files[1][0], bitmaps[1][0], 0, count);
		test_progressive_decode(progressive, files[1][1], bitmaps[1][1], 1, count);
		test_progressive_decode(progressive, files[1][2], bitmaps[1][2], 2, count);
		test_progressive_decode(progressive, files[1][3], bitmaps[1][3], 3, count);
	}

	/* image 3 */

	if (0)
	{
		printf("\nSample Image 3\n"); /* sample data is in incorrect order */
		test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
		test_progressive_decode(progressive, files[2][0], bitmaps[2][0], 0, count);
		test_progressive_decode(progressive, files[2][1], bitmaps[2][1], 1, count);
		test_progressive_decode(progressive, files[2][2], bitmaps[2][2], 2, count);
		test_progressive_decode(progressive, files[2][3], bitmaps[2][3], 3, count);
	}

	progressive_context_free(progressive);

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			for (int k = 0; k < 4; k++)
			{
				sample_file_free(&bitmaps[i][j][k]);
				sample_file_free(&files[i][j][k]);
			}
		}
	}

	winpr_aligned_free(g_DstData);
	return 0;
}

static BOOL diff(BYTE a, BYTE b)
{
	BYTE big = MAX(a, b);
	BYTE little = MIN(a, b);
	if (big - little <= 0x25)
		return TRUE;
	return FALSE;
}

static BOOL colordiff(UINT32 format, UINT32 a, UINT32 b)
{
	BYTE ar = 0;
	BYTE ag = 0;
	BYTE ab = 0;
	BYTE aa = 0;
	BYTE br = 0;
	BYTE bg = 0;
	BYTE bb = 0;
	BYTE ba = 0;
	FreeRDPSplitColor(a, format, &ar, &ag, &ab, &aa, NULL);
	FreeRDPSplitColor(b, format, &br, &bg, &bb, &ba, NULL);
	if (!diff(aa, ba) || !diff(ar, br) || !diff(ag, bg) || !diff(ab, bb))
		return FALSE;
	return TRUE;
}

static BOOL test_encode_decode(const char* path)
{
	BOOL res = FALSE;
	int rc = 0;
	BYTE* resultData = NULL;
	BYTE* dstData = NULL;
	UINT32 dstSize = 0;
	UINT32 ColorFormat = PIXEL_FORMAT_BGRX32;
	REGION16 invalidRegion = { 0 };
	wImage* image = winpr_image_new();
	wImage* dstImage = winpr_image_new();
	char* name = GetCombinedPath(path, "progressive.bmp");
	PROGRESSIVE_CONTEXT* progressiveEnc = progressive_context_new(TRUE);
	PROGRESSIVE_CONTEXT* progressiveDec = progressive_context_new(FALSE);

	region16_init(&invalidRegion);
	if (!image || !dstImage || !name || !progressiveEnc || !progressiveDec)
		goto fail;

	rc = winpr_image_read(image, name);
	if (rc <= 0)
		goto fail;

	resultData = calloc(image->scanline, image->height);
	if (!resultData)
		goto fail;

	// Progressive encode
	rc = progressive_compress(progressiveEnc, image->data, image->scanline * image->height,
	                          ColorFormat, image->width, image->height, image->scanline, NULL,
	                          &dstData, &dstSize);

	// Progressive decode
	rc = progressive_create_surface_context(progressiveDec, 0, image->width, image->height);
	if (rc <= 0)
		goto fail;

	rc = progressive_decompress(progressiveDec, dstData, dstSize, resultData, ColorFormat,
	                            image->scanline, 0, 0, &invalidRegion, 0, 0);
	if (rc < 0)
		goto fail;

	// Compare result
	if (0) // Dump result image for manual inspection
	{
		*dstImage = *image;
		dstImage->data = resultData;
		winpr_image_write(dstImage, "/tmp/test.bmp");
	}
	for (UINT32 y = 0; y < image->height; y++)
	{
		const BYTE* orig = &image->data[y * image->scanline];
		const BYTE* dec = &resultData[y * image->scanline];
		for (UINT32 x = 0; x < image->width; x++)
		{
			const BYTE* po = &orig[x * 4];
			const BYTE* pd = &dec[x * 4];

			const DWORD a = FreeRDPReadColor(po, ColorFormat);
			const DWORD b = FreeRDPReadColor(pd, ColorFormat);
			if (!colordiff(ColorFormat, a, b))
			{
				printf("xxxxxxx [%u:%u] [%s] %08X != %08X\n", x, y,
				       FreeRDPGetColorFormatName(ColorFormat), a, b);
				goto fail;
			}
		}
	}
	res = TRUE;
fail:
	region16_uninit(&invalidRegion);
	progressive_context_free(progressiveEnc);
	progressive_context_free(progressiveDec);
	winpr_image_free(image, TRUE);
	winpr_image_free(dstImage, FALSE);
	free(resultData);
	free(name);
	return res;
}

int TestFreeRDPCodecProgressive(int argc, char* argv[])
{
	int rc = -1;
	char* ms_sample_path = NULL;
	char name[8192];
	SYSTEMTIME systemTime;
	WINPR_UNUSED(argc);
	WINPR_UNUSED(argv);

	GetSystemTime(&systemTime);
	sprintf_s(name, sizeof(name),
	          "EGFX_PROGRESSIVE_MS_SAMPLE-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16
	          "%02" PRIu16 "%02" PRIu16 "%04" PRIu16,
	          systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
	          systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
	ms_sample_path = _strdup(CMAKE_CURRENT_SOURCE_DIR);

	if (!ms_sample_path)
	{
		printf("Memory allocation failed\n");
		goto fail;
	}

	if (winpr_PathFileExists(ms_sample_path))
	{
		/*
		if (test_progressive_ms_sample(ms_sample_path) < 0)
		    goto fail;
		    */
		if (!test_encode_decode(ms_sample_path))
			goto fail;
		rc = 0;
	}

fail:
	free(ms_sample_path);
	return rc;
}
