/**
 * FreeRDP: A Remote Desktop Protocol Implementation
 * Color Conversion Routines
 *
 * Copyright 2010 Marc-Andre Moreau <marcandre.moreau@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <winpr/crt.h>

#include <freerdp/log.h>
#include <freerdp/freerdp.h>
#include <freerdp/primitives.h>

#define TAG FREERDP_TAG("color")

int freerdp_get_pixel(BYTE* data, int x, int y, int width, int height, int bpp)
{
	int start;
	int shift;
	UINT16* src16;
	UINT32* src32;
	int red, green, blue;

	switch (bpp)
	{
		case  1:
			width = (width + 7) / 8;
			start = (y * width) + x / 8;
			shift = x % 8;
			return (data[start] & (0x80 >> shift)) != 0;
		case 8:
			return data[y * width + x];
		case 15:
		case 16:
			src16 = (UINT16*) data;
			return src16[y * width + x];
		case 24:
			data += y * width * 3;
			data += x * 3;
			red = data[0];
			green = data[1];
			blue = data[2];
			return RGB24(red, green, blue);
		case 32:
			src32 = (UINT32*) data;
			return src32[y * width + x];
		default:
			break;
	}

	return 0;
}

void freerdp_set_pixel(BYTE* data, int x, int y, int width, int height, int bpp, int pixel)
{
	int start;
	int shift;
	int *dst32;

	if (bpp == 1)
	{
		width = (width + 7) / 8;
		start = (y * width) + x / 8;
		shift = x % 8;
		if (pixel)
			data[start] = data[start] | (0x80 >> shift);
		else
			data[start] = data[start] & ~(0x80 >> shift);
	}
	else if (bpp == 32)
	{
		dst32 = (int*) data;
		dst32[y * width + x] = pixel;
	}
}

static INLINE void freerdp_color_split_rgb(UINT32* color, int bpp, BYTE* red, BYTE* green, BYTE* blue, BYTE* alpha, HCLRCONV clrconv)
{
	*red = *green = *blue = 0;
	*alpha = (clrconv->alpha) ? 0xFF : 0x00;

	switch (bpp)
	{
		case 32:
			if (clrconv->alpha)
			{
				GetARGB32(*alpha, *red, *green, *blue, *color);
			}
			else
			{
				GetRGB32(*red, *green, *blue, *color);
			}
			break;

		case 24:
			GetRGB24(*red, *green, *blue, *color);
			break;

		case 16:
			GetRGB16(*red, *green, *blue, *color);
			break;

		case 15:
			GetRGB15(*red, *green, *blue, *color);
			break;

		case 8:
			*color &= 0xFF;
			*red = clrconv->palette->entries[*color].red;
			*green = clrconv->palette->entries[*color].green;
			*blue = clrconv->palette->entries[*color].blue;
			break;

		case 1:
			if (*color != 0)
			{
				*red = 0xFF;
				*green = 0xFF;
				*blue = 0xFF;
			}
			break;

		default:
			break;
	}
}

static INLINE void freerdp_color_split_bgr(UINT32* color, int bpp, BYTE* red, BYTE* green, BYTE* blue, BYTE* alpha, HCLRCONV clrconv)
{
	*red = *green = *blue = 0;
	*alpha = (clrconv->alpha) ? 0xFF : 0x00;

	switch (bpp)
	{
		case 32:
			if (clrconv->alpha)
			{
				GetABGR32(*alpha, *red, *green, *blue, *color);
			}
			else
			{
				GetBGR32(*red, *green, *blue, *color);
			}
			break;

		case 24:
			GetBGR24(*red, *green, *blue, *color);
			break;

		case 16:
			GetBGR16(*red, *green, *blue, *color);
			break;

		case 15:
			GetBGR15(*red, *green, *blue, *color);
			break;

		case 8:
			*color &= 0xFF;
			*red = clrconv->palette->entries[*color].red;
			*green = clrconv->palette->entries[*color].green;
			*blue = clrconv->palette->entries[*color].blue;
			break;

		case 1:
			if (*color != 0)
			{
				*red = 0xFF;
				*green = 0xFF;
				*blue = 0xFF;
			}
			break;

		default:
			break;
	}
}

static INLINE void freerdp_color_make_rgb(UINT32* color, int bpp, BYTE* red, BYTE* green, BYTE* blue, BYTE* alpha, HCLRCONV clrconv)
{
	switch (bpp)
	{
		case 32:
			*color = ARGB32(*alpha, *red, *green, *blue);
			break;

		case 24:
			*color = RGB24(*red, *green, *blue);
			break;

		case 16:
			if (clrconv->rgb555)
			{
				*color = RGB15(*red, *green, *blue);
			}
			else
			{
				*color = RGB16(*red, *green, *blue);
			}
			break;

		case 15:
			*color = RGB15(*red, *green, *blue);
			break;

		case 8:
			*color = RGB24(*red, *green, *blue);
			break;

		case 1:
			if ((*red != 0) || (*green != 0) || (*blue != 0))
				*color = 1;
			break;

		default:
			break;
	}
}

static INLINE void freerdp_color_make_bgr(UINT32* color, int bpp, BYTE* red, BYTE* green, BYTE* blue, BYTE* alpha, HCLRCONV clrconv)
{
	switch (bpp)
	{
		case 32:
			*color = ABGR32(*alpha, *red, *green, *blue);
			break;

		case 24:
			*color = BGR24(*red, *green, *blue);
			break;

		case 16:
			if (clrconv->rgb555)
			{
				*color = BGR15(*red, *green, *blue);
			}
			else
			{
				*color = BGR16(*red, *green, *blue);
			}
			break;

		case 15:
			*color = BGR15(*red, *green, *blue);
			break;

		case 8:
			*color = BGR24(*red, *green, *blue);
			break;

		case 1:
			if ((*red != 0) || (*green != 0) || (*blue != 0))
				*color = 1;
			break;

		default:
			break;
	}
}

UINT32 freerdp_color_convert_rgb(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	BYTE red = 0;
	BYTE green = 0;
	BYTE blue = 0;
	BYTE alpha = 0xFF;
	UINT32 dstColor = 0;

	freerdp_color_split_rgb(&srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv);
	freerdp_color_make_rgb(&dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv);

	return dstColor;
}

UINT32 freerdp_color_convert_bgr(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	BYTE red = 0;
	BYTE green = 0;
	BYTE blue = 0;
	BYTE alpha = 0xFF;
	UINT32 dstColor = 0;

	freerdp_color_split_bgr(&srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv);
	freerdp_color_make_bgr(&dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv);

	return dstColor;
}

UINT32 freerdp_color_convert_rgb_bgr(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	BYTE red = 0;
	BYTE green = 0;
	BYTE blue = 0;
	BYTE alpha = 0xFF;
	UINT32 dstColor = 0;

	freerdp_color_split_rgb(&srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv);
	freerdp_color_make_bgr(&dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv);

	return dstColor;
}

UINT32 freerdp_color_convert_bgr_rgb(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	BYTE red = 0;
	BYTE green = 0;
	BYTE blue = 0;
	BYTE alpha = 0xFF;
	UINT32 dstColor = 0;

	freerdp_color_split_bgr(&srcColor, srcBpp, &red, &green, &blue, &alpha, clrconv);
	freerdp_color_make_rgb(&dstColor, dstBpp, &red, &green, &blue, &alpha, clrconv);

	return dstColor;
}

UINT32 freerdp_color_convert_var(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	if (clrconv->invert)
		return freerdp_color_convert_var_bgr(srcColor, srcBpp, dstBpp, clrconv);
	else
		return freerdp_color_convert_var_rgb(srcColor, srcBpp, dstBpp, clrconv);
}

UINT32 freerdp_color_convert_var_rgb(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	if (srcBpp == 8)
	{
		BYTE alpha = 0xFF;
		UINT32 dstColor = 0;
		PALETTE_ENTRY* entry = &clrconv->palette->entries[srcColor & 0xFF];

		freerdp_color_make_bgr(&dstColor, dstBpp, &entry->red, &entry->green, &entry->blue, &alpha, clrconv);

		return dstColor;
	}

	if (srcBpp > 16)
		return freerdp_color_convert_bgr_rgb(srcColor, srcBpp, dstBpp, clrconv);
	else
		return freerdp_color_convert_rgb(srcColor, srcBpp, dstBpp, clrconv);
}

UINT32 freerdp_color_convert_var_bgr(UINT32 srcColor, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	if (srcBpp == 8)
	{
		BYTE alpha = 0xFF;
		UINT32 dstColor = 0;
		PALETTE_ENTRY* entry = &clrconv->palette->entries[srcColor & 0xFF];

		freerdp_color_make_rgb(&dstColor, dstBpp, &entry->red, &entry->green, &entry->blue, &alpha, clrconv);

		return dstColor;
	}

	if (srcBpp > 16)
		return freerdp_color_convert_bgr(srcColor, srcBpp, dstBpp, clrconv);
	else
		return freerdp_color_convert_rgb_bgr(srcColor, srcBpp, dstBpp, clrconv);
}

UINT32 freerdp_convert_gdi_order_color(UINT32 color, int bpp, UINT32 format, BYTE* palette)
{
	UINT32 r = 0;
	UINT32 g = 0;
	UINT32 b = 0;

	switch (bpp)
	{
		case 32:
			GetRGB32(r, g, b, color);
			break;

		case 24:
			GetRGB32(r, g, b, color);
			break;

		case 16:
			color = (color & (UINT32) 0xFF00) | ((color >> 16) & (UINT32) 0xFF);
			GetRGB16(r, g, b, color);
			break;

		case 15:
			color = (color & (UINT32) 0xFF00) | ((color >> 16) & (UINT32) 0xFF);
			GetRGB15(r, g, b, color);
			break;

		case 8:
			color = (color >> 16) & (UINT32) 0xFF;
			if (palette)
			{
				r = palette[(color * 4) + 2];
				g = palette[(color * 4) + 1];
				b = palette[(color * 4) + 0];
			}
			break;

		case 1:
			r = g = b = 0;
			if (color != 0)
				r = g = b = 0xFF;
			break;

		default:
			return color;
			break;
	}

	if (FREERDP_PIXEL_FORMAT_TYPE(format) == FREERDP_PIXEL_FORMAT_TYPE_ABGR)
		return BGR32(r, g, b);

	return RGB32(r, g, b);
}

BYTE* freerdp_image_convert_8bpp(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	int i;
	BYTE red;
	BYTE green;
	BYTE blue;
	UINT32 pixel;
	BYTE *src8;
	UINT16 *dst16;
	UINT32 *dst32;

	if (dstBpp == 8)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height, 16);

		if (!dstData)
			return NULL;

		CopyMemory(dstData, srcData, width * height);

		return dstData;
	}
	else if (dstBpp == 15 || (dstBpp == 16 && clrconv->rgb555))
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		dst16 = (UINT16*) dstData;

		for (i = width * height; i > 0; i--)
		{
			pixel = *srcData;
			srcData++;
			red = clrconv->palette->entries[pixel].red;
			green = clrconv->palette->entries[pixel].green;
			blue = clrconv->palette->entries[pixel].blue;
			pixel = (clrconv->invert) ? BGR15(red, green, blue) : RGB15(red, green, blue);
			*dst16 = pixel;
			dst16++;
		}

		return dstData;
	}
	else if (dstBpp == 16)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		dst16 = (UINT16*) dstData;

		for (i = width * height; i > 0; i--)
		{
			pixel = *srcData;
			srcData++;
			red = clrconv->palette->entries[pixel].red;
			green = clrconv->palette->entries[pixel].green;
			blue = clrconv->palette->entries[pixel].blue;
			pixel = (clrconv->invert) ? BGR16(red, green, blue) : RGB16(red, green, blue);
			*dst16 = pixel;
			dst16++;
		}

		return dstData;
	}
	else if (dstBpp == 32)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 4, 16);

		if (!dstData)
			return NULL;

		src8 = (BYTE*) srcData;
		dst32 = (UINT32*) dstData;

		for (i = width * height; i > 0; i--)
		{
			pixel = *src8;
			src8++;

			red = clrconv->palette->entries[pixel].red;
			green = clrconv->palette->entries[pixel].green;
			blue = clrconv->palette->entries[pixel].blue;

			if (clrconv->alpha)
			{
				pixel = (clrconv->invert) ? ABGR32(0xFF, red, green, blue) : ARGB32(0xFF, red, green, blue);
			}
			else
			{
				pixel = (clrconv->invert) ? BGR32(red, green, blue) : RGB32(red, green, blue);
			}

			*dst32 = pixel;
			dst32++;
		}

		return dstData;
	}

	return srcData;
}

BYTE* freerdp_image_convert_15bpp(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	int i;
	BYTE red;
	BYTE green;
	BYTE blue;
	UINT32 pixel;
	UINT16 *src16;
	UINT16 *dst16;
	UINT32 *dst32;

	if (dstBpp == 15 || (dstBpp == 16 && clrconv->rgb555))
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		CopyMemory(dstData, srcData, width * height * 2);

		return dstData;
	}
	else if (dstBpp == 32)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 4, 16);

		if (!dstData)
			return NULL;

		src16 = (UINT16*) srcData;
		dst32 = (UINT32*) dstData;

		for (i = width * height; i > 0; i--)
		{
			pixel = *src16;
			src16++;
			GetBGR15(red, green, blue, pixel);
			if (clrconv->alpha)
			{
				pixel = (clrconv->invert) ? ARGB32(0xFF, red, green, blue) : ABGR32(0xFF, red, green, blue);
			}
			else
			{
				pixel = (clrconv->invert) ? RGB32(red, green, blue) : BGR32(red, green, blue);
			}
			*dst32 = pixel;
			dst32++;
		}

		return dstData;
	}
	else if (dstBpp == 16)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		src16 = (UINT16*) srcData;
		dst16 = (UINT16*) dstData;

		for (i = width * height; i > 0; i--)
		{
			pixel = *src16;
			src16++;
			GetRGB_555(red, green, blue, pixel);
			RGB_555_565(red, green, blue);
			pixel = (clrconv->invert) ? BGR565(red, green, blue) : RGB565(red, green, blue);
			*dst16 = pixel;
			dst16++;
		}

		return dstData;
	}

	return srcData;
}

BYTE* freerdp_image_convert_16bpp(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	if (srcBpp == 15)
		return freerdp_image_convert_15bpp(srcData, dstData, width, height, srcBpp, dstBpp, clrconv);

	if (dstBpp == 16)
	{
		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		if (clrconv->rgb555)
		{
			int i;
			BYTE red, green, blue;
			UINT16* src16 = (UINT16 *) srcData;
			UINT16* dst16 = (UINT16 *) dstData;

			for (i = width * height; i > 0; i--)
			{
				GetRGB_565(red, green, blue, (*src16));
				RGB_565_555(red, green, blue);
				(*dst16) = (clrconv->invert) ? BGR555(red, green, blue) : RGB555(red, green, blue);
				src16++;
				dst16++;
			}
		}
		else
		{
			CopyMemory(dstData, srcData, width * height * 2);
		}

		return dstData;
	}
	else if (dstBpp == 24)
	{
		int i;
		BYTE *dst8;
		UINT16 *src16;
		BYTE red, green, blue;

		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 3, 16);

		if (!dstData)
			return NULL;

		dst8 = (BYTE*) dstData;
		src16 = (UINT16*) srcData;

		for (i = width * height; i > 0; i--)
		{
			GetBGR16(red, green, blue, *src16);
			src16++;

			if (clrconv->invert)
			{
				*dst8++ = blue;
				*dst8++ = green;
				*dst8++ = red;
			}
			else
			{
				*dst8++ = red;
				*dst8++ = green;
				*dst8++ = blue;
			}
		}

		return dstData;
	}
	else if (dstBpp == 32)
	{
		primitives_t* prims;

		if (!dstData)
			dstData = _aligned_malloc(width * height * sizeof(UINT32), 16);

		if (!dstData)
			return NULL;

		prims = primitives_get();
		prims->RGB565ToARGB_16u32u_C3C4(
			(const UINT16*) srcData, width * sizeof(UINT16),
			(UINT32*) dstData, width * sizeof(UINT32),
			width, height, clrconv->alpha, clrconv->invert);

		return dstData;
	}

	return srcData;
}

BYTE* freerdp_image_convert_24bpp(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	int i;

	if (dstBpp == 32)
	{
		UINT32 pixel, alpha_mask, temp;
		UINT32* srcp;
		UINT32* dstp;

		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 4, 16);

		if (!dstData)
			return NULL;

		alpha_mask = clrconv->alpha ? 0xFF000000 : 0;

		srcp = (UINT32*) srcData;
		dstp = (UINT32*) dstData;

		if (clrconv->invert)
		{
			/* Each iteration handles four pixels using 32-bit load and
			   store operations. */
			for (i = ((width * height) / 4); i > 0; i--)
			{
				temp = 0;

				pixel = temp;
				temp = *srcp++;
				pixel |= temp & 0x00FFFFFF;
				temp = temp >> 24;
				*dstp++ = alpha_mask | RGB32_to_BGR32(pixel);

				pixel = temp;
				temp = *srcp++;
				pixel |= (temp & 0x0000FFFF) << 8;
				temp = temp >> 16;
				*dstp++ = alpha_mask | RGB32_to_BGR32(pixel);

				pixel = temp;
				temp = *srcp++;
				pixel |= (temp & 0x000000FF) << 16;
				temp = temp >> 8;
				*dstp++ = alpha_mask | RGB32_to_BGR32(pixel);

				*dstp++ = alpha_mask | RGB32_to_BGR32(temp);
			}

			/* Handle any remainder. */
			for (i = (width * height) % 4; i > 0; i--)
			{
				pixel = ABGR32(alpha_mask, srcData[2], srcData[1], srcData[0]);
				*dstp++ = pixel;
				srcData += 3;
			}
		}
		else
		{
			for (i = ((width * height) / 4); i > 0; i--)
			{
				temp = 0;

				pixel = temp;
				temp = *srcp++;
				pixel |= temp & 0x00FFFFFF;
				temp = temp >> 24;
				*dstp++ = alpha_mask | pixel;

				pixel = temp;
				temp = *srcp++;
				pixel |= (temp & 0x0000FFFF) << 8;
				temp = temp >> 16;
				*dstp++ = alpha_mask | pixel;

				pixel = temp;
				temp = *srcp++;
				pixel |= (temp & 0x000000FF) << 16;
				temp = temp >> 8;
				*dstp++ = alpha_mask | pixel;

				*dstp++ = alpha_mask | temp;
			}

			for (i = (width * height) % 4; i > 0; i--)
			{
				pixel = ARGB32(alpha_mask, srcData[2], srcData[1], srcData[0]);
				*dstp++ = pixel;
				srcData += 3;
			}
		}

		return dstData;
	}

	return srcData;
}

BYTE* freerdp_image_convert_32bpp(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	if (dstBpp == 16)
	{
		int index;
		UINT16 *dst16;
		UINT32 *src32;
		BYTE red, green, blue;

		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		dst16 = (UINT16*) dstData;
		src32 = (UINT32*) srcData;

		for (index = 0; index < width * height; index++)
		{
			GetBGR32(blue, green, red, *src32);
			*dst16 = (clrconv->invert) ? BGR16(red, green, blue) : RGB16(red, green, blue);
			src32++;
			dst16++;
		}

		return dstData;
	}
	else if (dstBpp == 24)
	{
		int index;
		BYTE* dstp;
		BYTE red, green, blue;

		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 3, 16);

		if (!dstData)
			return NULL;

		dstp = dstData;

		for (index = 0; index < width * height; index++)
		{
			red = *(srcData++);
			green = *(srcData++);
			blue = *(srcData++);

			if (clrconv->invert)
			{
				*dstp++ = blue;
				*dstp++ = green;
				*dstp++ = red;
			}
			else
			{
				*dstp++ = red;
				*dstp++ = green;
				*dstp++ = blue;
			}

			srcData++;
		}

		return dstData;
	}
	else if (dstBpp == 32)
	{
		int i;
		UINT32 pixel;
		UINT32 alpha_mask;
		UINT32* srcp;
		UINT32* dstp;
		BYTE red, green, blue;

		if (!dstData)
			dstData = (BYTE*) _aligned_malloc(width * height * 4, 16);

		if (!dstData)
			return NULL;

		alpha_mask = clrconv->alpha ? 0xFF000000 : 0;

		srcp = (UINT32*) srcData;
		dstp = (UINT32*) dstData;

		if (clrconv->invert)
		{
			for (i = width * height; i > 0; i--)
			{
				pixel = *srcp;
				srcp++;
				GetRGB32(red, green, blue, pixel);
				pixel = alpha_mask | BGR32(red, green, blue);
				*dstp = pixel;
				dstp++;
			}
		}
		else
		{
			for (i = width * height; i > 0; i--)
			{
				pixel = *srcp;
				srcp++;
				GetRGB32(red, green, blue, pixel);
				pixel = alpha_mask | RGB32(red, green, blue);
				*dstp = pixel;
				dstp++;
			}
		}

		return dstData;
	}

	return srcData;
}

p_freerdp_image_convert freerdp_image_convert_[5] =
{
	NULL,
	freerdp_image_convert_8bpp,
	freerdp_image_convert_16bpp,
	freerdp_image_convert_24bpp,
	freerdp_image_convert_32bpp
};

BYTE* freerdp_image_convert(BYTE* srcData, BYTE* dstData, int width, int height, int srcBpp, int dstBpp, HCLRCONV clrconv)
{
	p_freerdp_image_convert _p_freerdp_image_convert = freerdp_image_convert_[IBPP(srcBpp)];

	if (_p_freerdp_image_convert != NULL)
		return _p_freerdp_image_convert(srcData, dstData, width, height, srcBpp, dstBpp, clrconv);
	else
		return 0;
}

void freerdp_bitmap_flip(BYTE * src, BYTE * dst, int scanLineSz, int height)
{
	int i;

	BYTE * bottomLine = dst + (scanLineSz * (height - 1));
	BYTE * topLine = src;

	/* Special processing if called for flip-in-place. */
	if (src == dst)
	{
		/* Allocate a scanline buffer.
		 * (FIXME: malloc / xfree below should be replaced by "get/put
		 * scanline buffer from a pool/Q of fixed buffers" to reuse
		 * fixed size buffers (of max scanline size (or adaptative?) )
		 * -- would be much faster).
		 */
		BYTE* tmpBfr = _aligned_malloc(scanLineSz, 16);
		int half = height / 2;
		/* Flip buffer in place by line permutations through the temp
		 * scan line buffer.
		 * Note that if height has an odd number of line, we don't need
		 * to move the center scanline anyway.
		 * Also note that in place flipping takes three memcpy() calls
		 * to process two scanlines while src to distinct dest would
		 * only requires two memcpy() calls for two scanlines.
		 */
		height--;
		for (i = 0; i < half ; i++)
		{
			CopyMemory(tmpBfr, topLine, scanLineSz);
			CopyMemory(topLine, bottomLine, scanLineSz);
			CopyMemory(bottomLine, tmpBfr, scanLineSz);
			topLine += scanLineSz;
			bottomLine -= scanLineSz;
			height--;
		}
		_aligned_free(tmpBfr);
	}
	/* Flip from source buffer to destination buffer. */
	else
	{

		for (i = 0; i < height; i++)
		{
			CopyMemory(bottomLine, topLine, scanLineSz);
			topLine += scanLineSz;
			bottomLine -= scanLineSz;
		}
	}

}

BYTE* freerdp_image_flip(BYTE* srcData, BYTE* dstData, int width, int height, int bpp)
{
	int scanline;

	scanline = width * ((bpp + 7) / 8);

	if (!dstData)
		dstData = (BYTE*) _aligned_malloc(width * height * ((bpp + 7) / 8), 16);

	if (!dstData)
		return NULL;

	freerdp_bitmap_flip(srcData, dstData, scanline, height);

	return dstData;
}

BYTE* freerdp_icon_convert(BYTE* srcData, BYTE* dstData, BYTE* mask, int width, int height, int bpp, HCLRCONV clrconv)
{
	BYTE* data;
	BYTE bmask;
	UINT32* icon;
	int x, y, bit;
	int maskIndex;

	if (bpp == 16)
	{
		/* Server sends 16 bpp field, but data is usually 15-bit 555 */
		bpp = 15;
	}
	
	data = freerdp_image_flip(srcData, dstData, width, height, bpp);
	dstData = freerdp_image_convert(data, NULL, width, height, bpp, 32, clrconv);
	_aligned_free(data);

	/* Read the AND alpha plane */ 
	if (bpp < 32)
	{
		maskIndex = 0;
		icon = (UINT32*) dstData;
		
		for (y = 0; y < height; y++)
		{
			for (x = 0; x < width-7; x+=8)
			{
				bmask = mask[maskIndex++];
				
				for (bit = 0; bit < 8; bit++)
					if ((bmask & (0x80 >> bit)) == 0)
					{
						UINT32 *tmp = (icon + (height - y - 1) * width + x + bit);
						if (tmp)
							*tmp |= 0xFF000000;
					}
			}
			
			if ((width % 8) != 0)
			{
				bmask = mask[maskIndex++];
				
				for (bit = 0; bit < width % 8; bit++)
					if ((bmask & (0x80 >> bit)) == 0)
					{
						UINT32 *tmp = (icon + (height - y - 1) * width + x + bit);
						if (tmp)
							*tmp |= 0xFF000000;
					}
			}
		
			/* Skip padding */
			if ((width % 32) != 0)
				maskIndex += (32 - (width % 32)) / 8;
		}
	}

	return dstData;
}

BYTE* freerdp_glyph_convert(int width, int height, BYTE* data)
{
	int x, y;
	BYTE* srcp;
	BYTE* dstp;
	BYTE* dstData;
	int scanline;

	/*
	 * converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph:
	 * this approach uses a little more memory, but provides faster
	 * means of accessing individual pixels in blitting operations
	 */

	scanline = (width + 7) / 8;
	dstData = (BYTE*) _aligned_malloc(width * height, 16);

	if (!dstData)
		return NULL;

	ZeroMemory(dstData, width * height);
	dstp = dstData;

	for (y = 0; y < height; y++)
	{
		srcp = data + (y * scanline);

		for (x = 0; x < width; x++)
		{
			if ((*srcp & (0x80 >> (x % 8))) != 0)
				*dstp = 0xFF;
			dstp++;

			if (((x + 1) % 8 == 0) && x != 0)
				srcp++;
		}
	}

	return dstData;
}

BYTE* freerdp_mono_image_convert(BYTE* srcData, int width, int height, int srcBpp, int dstBpp, UINT32 bgcolor, UINT32 fgcolor, HCLRCONV clrconv)
{
	int index;
	UINT16* dst16;
	UINT32* dst32;
	BYTE* dstData;
	BYTE bitMask;
	int bitIndex;
	BYTE redBg, greenBg, blueBg;
	BYTE redFg, greenFg, blueFg;

	GetRGB32(redBg, greenBg, blueBg, bgcolor);
	GetRGB32(redFg, greenFg, blueFg, fgcolor);

	if (dstBpp == 16)
	{
		dstData = (BYTE*) _aligned_malloc(width * height * 2, 16);

		if (!dstData)
			return NULL;

		dst16 = (UINT16*) dstData;

		if (clrconv->rgb555)
		{
			bgcolor = clrconv->invert ? BGR15(redBg, greenBg, blueBg) : RGB15(redBg, greenBg, blueBg);
			fgcolor = clrconv->invert ? BGR15(redFg, greenFg, blueFg) : RGB15(redFg, greenFg, blueFg);
		}
		else
		{
			bgcolor = clrconv->invert ? BGR16(redBg, greenBg, blueBg) : RGB16(redBg, greenBg, blueBg);
			fgcolor = clrconv->invert ? BGR16(redFg, greenFg, blueFg) : RGB16(redFg, greenFg, blueFg);
		}

		for (index = height; index > 0; index--)
		{
			/* each bit encodes a pixel */
			bitMask = *srcData;
			for (bitIndex = 7; bitIndex >= 0; bitIndex--)
			{
				if ((bitMask >> bitIndex) & 0x01)
				{
					*dst16 = bgcolor;
				}
				else
				{
					*dst16 = fgcolor;
				}
				dst16++;
			}
			srcData++;
		}
		return dstData;
	}
	else if (dstBpp == 32)
	{
		dstData = (BYTE*) _aligned_malloc(width * height * 4, 16);

		if (!dstData)
			return NULL;

		dst32 = (UINT32*) dstData;

		for (index = height; index > 0; index--)
		{
			/* each bit encodes a pixel */
			bitMask = *srcData;

			for (bitIndex = 7; bitIndex >= 0; bitIndex--)
			{
				if ((bitMask >> bitIndex) & 0x01)
				{
					if (clrconv->alpha)
					{
						*dst32 = (clrconv->invert) ? ABGR32(0xFF, redBg, greenBg, blueBg) : ARGB32(0xFF, redBg, greenBg, blueBg);
					}
					else
					{
						*dst32 = (clrconv->invert) ? BGR32(redBg, greenBg, blueBg) : RGB32(redBg, greenBg, blueBg);
					}
				}
				else
				{
					if (clrconv->alpha)
					{
						*dst32 = (clrconv->invert) ? ABGR32(0xFF, redFg, greenFg, blueFg) : ARGB32(0xFF, redFg, greenFg, blueFg);
					}
					else
					{
						*dst32 = (clrconv->invert) ? BGR32(redFg, greenFg, blueFg) : RGB32(redFg, greenFg, blueFg);
					}
				}
				dst32++;
			}
			srcData++;
		}
		return dstData;
	}

	return srcData;
}

int freerdp_image_copy_from_monochrome(BYTE* pDstData, UINT32 DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, UINT32 backColor, UINT32 foreColor, BYTE* palette)
{
	int x, y;
	BOOL vFlip;
	BOOL invert;
	int srcFlip;
	int dstFlip;
	int nDstPad;
	int monoStep;
	UINT32 monoBit;
	BYTE* monoBits;
	UINT32 monoPixel;
	BYTE a, r, g, b;
	int dstBitsPerPixel;
	int dstBytesPerPixel;

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);

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

	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	srcFlip = FREERDP_PIXEL_FLIP_NONE;
	vFlip = (srcFlip != dstFlip) ? TRUE : FALSE;

	invert = (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat)) ? TRUE : FALSE;

	backColor |= 0xFF000000;
	foreColor |= 0xFF000000;

	monoStep = (nWidth + 7) / 8;

	if (dstBytesPerPixel == 4)
	{
		UINT32* pDstPixel;

		if (invert)
		{
			GetARGB32(a, r, g, b, backColor);
			backColor = ABGR32(a, r, g, b);

			GetARGB32(a, r, g, b, foreColor);
			foreColor = ABGR32(a, r, g, b);
		}

		pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

		for (y = 0; y < nHeight; y++)
		{
			monoBit = 0x80;

			if (!vFlip)
				monoBits = &pSrcData[monoStep * y];
			else
				monoBits = &pSrcData[monoStep * (nHeight - y - 1)];

			for (x = 0; x < nWidth; x++)
			{
				monoPixel = (*monoBits & monoBit) ? 1 : 0;
				if (!(monoBit >>= 1)) { monoBits++; monoBit = 0x80; }

				if (monoPixel)
					*pDstPixel++ = backColor;
				else
					*pDstPixel++ = foreColor;
			}

			pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
		}

		return 1;
	}
	else if (dstBytesPerPixel == 2)
	{
		UINT16* pDstPixel;
		UINT16 backColor16;
		UINT16 foreColor16;

		if (!invert)
		{
			if (dstBitsPerPixel == 15)
			{
				GetRGB32(r, g, b, backColor);
				backColor16 = RGB15(r, g, b);

				GetRGB32(r, g, b, foreColor);
				foreColor16 = RGB15(r, g, b);
			}
			else
			{
				GetRGB32(r, g, b, backColor);
				backColor16 = RGB16(r, g, b);

				GetRGB32(r, g, b, foreColor);
				foreColor16 = RGB16(r, g, b);
			}
		}
		else
		{
			if (dstBitsPerPixel == 15)
			{
				GetRGB32(r, g, b, backColor);
				backColor16 = BGR15(r, g, b);

				GetRGB32(r, g, b, foreColor);
				foreColor16 = BGR15(r, g, b);
			}
			else
			{
				GetRGB32(r, g, b, backColor);
				backColor16 = BGR16(r, g, b);

				GetRGB32(r, g, b, foreColor);
				foreColor16 = BGR16(r, g, b);
			}
		}

		pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

		for (y = 0; y < nHeight; y++)
		{
			monoBit = 0x80;

			if (!vFlip)
				monoBits = &pSrcData[monoStep * y];
			else
				monoBits = &pSrcData[monoStep * (nHeight - y - 1)];

			for (x = 0; x < nWidth; x++)
			{
				monoPixel = (*monoBits & monoBit) ? 1 : 0;
				if (!(monoBit >>= 1)) { monoBits++; monoBit = 0x80; }

				if (monoPixel)
					*pDstPixel++ = backColor16;
				else
					*pDstPixel++ = foreColor16;
			}

			pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
		}

		return 1;
	}

	WLog_ERR(TAG, "failure: dstBytesPerPixel: %d dstBitsPerPixel: %d",
			dstBytesPerPixel, dstBitsPerPixel);

	return -1;
}

void freerdp_alpha_cursor_convert(BYTE* alphaData, BYTE* xorMask, BYTE* andMask, int width, int height, int bpp, HCLRCONV clrconv)
{
	UINT32 xorPixel;
	UINT32 andPixel;
	UINT32 x, y, jj;

	for (y = 0; y < height; y++)
	{
		jj = (bpp == 1) ? y : (height - 1) - y;

		for (x = 0; x < width; x++)
		{
			xorPixel = freerdp_get_pixel(xorMask, x, jj, width, height, bpp);
			xorPixel = freerdp_color_convert_rgb(xorPixel, bpp, 32, clrconv);
			andPixel = freerdp_get_pixel(andMask, x, jj, width, height, 1);

			if (andPixel)
			{
				if ((xorPixel & 0xFFFFFF) == 0xFFFFFF)
				{
					/* use pattern (not solid black) for xor area */
					xorPixel = (x & 1) == (y & 1);
					xorPixel = xorPixel ? 0xFFFFFF : 0;
					xorPixel |= 0xFF000000;
				}
				else if (xorPixel == 0xFF000000)
				{
					xorPixel = 0;
				}
			}

			freerdp_set_pixel(alphaData, x, y, width, height, 32, xorPixel);
		}
	}
}

/**
 * Drawing Monochrome Pointers:
 * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556143/
 *
 * Drawing Color Pointers:
 * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556138/
 */

int freerdp_image_copy_from_pointer_data(BYTE* pDstData, UINT32 DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* xorMask, BYTE* andMask, UINT32 xorBpp, BYTE* palette)
{
	int x, y;
	BOOL vFlip;
	BOOL invert;
	int srcFlip;
	int dstFlip;
	int nDstPad;
	int xorStep;
	int andStep;
	UINT32 xorBit;
	UINT32 andBit;
	BYTE* xorBits;
	BYTE* andBits;
	UINT32 xorPixel;
	UINT32 andPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);

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

	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	srcFlip = (xorBpp == 1) ? FREERDP_PIXEL_FLIP_NONE : FREERDP_PIXEL_FLIP_VERTICAL;

	vFlip = (srcFlip != dstFlip) ? TRUE : FALSE;
	invert = (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat)) ? TRUE : FALSE;

	andStep = (nWidth + 7) / 8;
	andStep += (andStep % 2);

	if (dstBytesPerPixel == 4)
	{
		UINT32* pDstPixel;

		if (xorBpp == 1)
		{
			xorStep = (nWidth + 7) / 8;
			xorStep += (xorStep % 2);

			pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

			for (y = 0; y < nHeight; y++)
			{
				xorBit = andBit = 0x80;

				if (!vFlip)
				{
					xorBits = &xorMask[xorStep * y];
					andBits = &andMask[andStep * y];
				}
				else
				{
					xorBits = &xorMask[xorStep * (nHeight - y - 1)];
					andBits = &andMask[andStep * (nHeight - y - 1)];
				}

				for (x = 0; x < nWidth; x++)
				{
					xorPixel = (*xorBits & xorBit) ? 1 : 0;
					if (!(xorBit >>= 1)) { xorBits++; xorBit = 0x80; }

					andPixel = (*andBits & andBit) ? 1 : 0;
					if (!(andBit >>= 1)) { andBits++; andBit = 0x80; }

					if (!andPixel && !xorPixel)
						*pDstPixel++ = 0xFF000000; /* black */
					else if (!andPixel && xorPixel)
						*pDstPixel++ = 0xFFFFFFFF; /* white */
					else if (andPixel && !xorPixel)
						*pDstPixel++ = 0x00000000; /* transparent */
					else if (andPixel && xorPixel)
						*pDstPixel++ = 0xFF000000; /* inverted (set as black) */
				}

				pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
			}

			return 1;
		}
		else if (xorBpp == 24 || xorBpp == 32)
		{
			int xorBytesPerPixel = xorBpp >> 3;
			xorStep = nWidth * xorBytesPerPixel;
			pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

			for (y = 0; y < nHeight; y++)
			{
				andBit = 0x80;

				if (!vFlip)
				{
					andBits = &andMask[andStep * y];
					xorBits = &xorMask[xorStep * y];
				}
				else
				{
					andBits = &andMask[andStep * (nHeight - y - 1)];
					xorBits = &xorMask[xorStep * (nHeight - y - 1)];
				}

				for (x = 0; x < nWidth; x++)
				{
					if (xorBpp == 32)
						xorPixel = *((UINT32*) xorBits);
					else
						xorPixel = xorBits[0] | xorBits[1] << 8 | xorBits[2] << 16 | 0xFF << 24;

					xorBits += xorBytesPerPixel;

					andPixel = (*andBits & andBit) ? 1 : 0;
					if (!(andBit >>= 1)) { andBits++; andBit = 0x80; }

					if (andPixel)
					{
						if (xorPixel == 0xFF000000) /* black */
							*pDstPixel++ = 0x00000000; /* transparent */
						else if (xorPixel == 0xFFFFFFFF) /* white */
							*pDstPixel++ = 0xFF000000; /* inverted (set as black) */
						else
							*pDstPixel++ = xorPixel;
					}
					else
					{
						*pDstPixel++ = xorPixel;
					}
				}

				pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
			}

			return 1;
		}
	}

	WLog_ERR(TAG, "failed to convert from %d bpp to %d bpp",
			xorBpp, dstBitsPerPixel);

	return -1;
}

void freerdp_image_swap_color_order(BYTE* data, int width, int height)
{
	int x, y;
	UINT32* pixel;
	BYTE a, r, g, b;

	pixel = (UINT32*) data;

	for (y = 0; y < height; y++)
	{
		for (x = 0; x < width; x++)
		{
			GetARGB32(a, r, g, b, *pixel);
			*pixel = ABGR32(a, r, g, b);
			pixel++;
		}
	}
}

HCLRCONV freerdp_clrconv_new(UINT32 flags)
{
	HCLRCONV clrconv;

	clrconv = (CLRCONV*) calloc(1, sizeof(CLRCONV));

	if (!clrconv)
		return NULL;

	clrconv->alpha = (flags & CLRCONV_ALPHA) ? TRUE : FALSE;
	clrconv->invert = (flags & CLRCONV_INVERT) ? TRUE : FALSE;
	clrconv->rgb555 = (flags & CLRCONV_RGB555) ? TRUE : FALSE;

	clrconv->palette = (rdpPalette*) calloc(1, sizeof(rdpPalette));

	if (!clrconv->palette)
	{
		free (clrconv);
		return NULL;
	}

	return clrconv;
}

void freerdp_clrconv_free(HCLRCONV clrconv)
{
	if (clrconv)
	{
		free(clrconv->palette);
		free(clrconv);
	}
}

int freerdp_image1_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int nSrcPad;
	int nDstPad;
	int nAlignedWidth;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);

	nAlignedWidth = nWidth + nWidth % 8;

	if (nSrcStep < 0)
		nSrcStep = nAlignedWidth / 8;

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

	nSrcPad = (nSrcStep - (nAlignedWidth / 8));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat))
		invert = TRUE;

	if (FREERDP_PIXEL_FORMAT_FLIP(DstFormat) == FREERDP_PIXEL_FLIP_VERTICAL)
		vFlip = TRUE;

	if (dstBytesPerPixel == 4)
	{
		BYTE SrcPixel;
		BYTE* pSrcPixel;
		UINT32* pDstPixel;

		if (!invert)
		{
			if (!vFlip)
			{
				pSrcPixel = &pSrcData[nYSrc * nSrcStep];
				pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth / 8; x++)
					{
						SrcPixel = *pSrcPixel;
						pDstPixel[0] = (SrcPixel & 0x80) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[1] = (SrcPixel & 0x40) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[2] = (SrcPixel & 0x20) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[3] = (SrcPixel & 0x10) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[4] = (SrcPixel & 0x08) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[5] = (SrcPixel & 0x04) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[6] = (SrcPixel & 0x02) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel[7] = (SrcPixel & 0x01) ? 0xFFFFFFFF : 0xFF000000;
						pDstPixel += 8;
						pSrcPixel++;
					}

					if (nWidth % 8)
					{
						SrcPixel = *pSrcPixel;

						for (x = 0; x < nWidth % 8; x++)
						{
							*pDstPixel = (SrcPixel & 0x80) ? 0xFFFFFFFF : 0xFF000000;
							SrcPixel <<= 1;
							pDstPixel++;
						}

						pSrcPixel++;
					}

					pSrcPixel += nSrcPad;
					pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
				}
			}
		}

		return 1;
	}

	return 1;
}

int freerdp_image4_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int nSrcPad;
	int nDstPad;
	int nAlignedWidth;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);

	nAlignedWidth = nWidth + (nWidth % 2);

	if (nSrcStep < 0)
		nSrcStep = nAlignedWidth / 2;

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

	nSrcPad = (nSrcStep - (nAlignedWidth / 2));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat))
		invert = TRUE;

	if (FREERDP_PIXEL_FORMAT_FLIP(DstFormat) == FREERDP_PIXEL_FLIP_VERTICAL)
		vFlip = TRUE;

	if (dstBytesPerPixel == 4)
	{
		BYTE* pSrcPixel;
		UINT32* pDstPixel;
		UINT32* values = (UINT32*) palette;

		if (!invert)
		{
			if (!vFlip)
			{
				pSrcPixel = &pSrcData[nYSrc * nSrcStep];
				pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth / 2; x++)
					{
						pDstPixel[0] = values[*pSrcPixel >> 4];
						pDstPixel[1] = values[*pSrcPixel & 0xF];
						pDstPixel += 2;
						pSrcPixel++;
					}

					if (nWidth % 2)
					{
						pDstPixel[0] = values[*pSrcPixel >> 4];
						pDstPixel++;
						pSrcPixel++;
					}

					pSrcPixel += nSrcPad;
					pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
				}
			}
		}

		return 1;
	}

	return 1;
}

int freerdp_image8_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	BYTE* pe;
	int x, y;
	int srcFlip;
	int dstFlip;
	int nSrcPad;
	int nDstPad;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	if (!palette)
		return -1;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);
	srcFlip = FREERDP_PIXEL_FORMAT_FLIP(SrcFormat);

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);

	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;

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

	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (srcFlip != dstFlip)
		vFlip = TRUE;

	invert = FREERDP_PIXEL_FORMAT_IS_ABGR(DstFormat) ? TRUE : FALSE;

	if (dstBytesPerPixel == 4)
	{
		if ((dstBitsPerPixel == 32) || (dstBitsPerPixel == 24))
		{
			BYTE* pSrcPixel;
			UINT32* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB32(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB32(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR32(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR32(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}
	else if (dstBytesPerPixel == 3)
	{

	}
	else if (dstBytesPerPixel == 2)
	{
		if (dstBitsPerPixel == 16)
		{
			BYTE* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB16(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB16(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR16(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR16(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
		else if (dstBitsPerPixel == 15)
		{
			BYTE* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB15(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = RGB15(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR15(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pe = &palette[*pSrcPixel * 4];
							*pDstPixel++ = BGR15(pe[2], pe[1], pe[0]);
							pSrcPixel++;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}
	else if (dstBytesPerPixel == 1)
	{
		BYTE* pSrcPixel;
		BYTE* pDstPixel;

		if (!vFlip)
		{
			pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + nXSrc];
			pDstPixel = &pDstData[(nYDst * nDstStep) + nXDst];

			for (y = 0; y < nHeight; y++)
			{
				CopyMemory(pDstPixel, pSrcPixel, nWidth);
				pSrcPixel = &pSrcPixel[nSrcStep];
				pDstPixel = &pDstPixel[nDstStep];
			}
		}
		else
		{
			pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + nXSrc];
			pDstPixel = &pDstData[(nYDst * nDstStep) + nXDst];

			for (y = 0; y < nHeight; y++)
			{
				CopyMemory(pDstPixel, pSrcPixel, nWidth);
				pSrcPixel = &pSrcPixel[-nSrcStep];
				pDstPixel = &pDstPixel[nDstStep];
			}
		}

		return 1;
	}

	return -1;
}

int freerdp_image15_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int srcFlip;
	int dstFlip;
	int nSrcPad;
	int nDstPad;
	BYTE r, g, b;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	int srcType, dstType;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);
	srcFlip = FREERDP_PIXEL_FORMAT_FLIP(SrcFormat);
	srcType = FREERDP_PIXEL_FORMAT_TYPE(SrcFormat);

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);
	dstType = FREERDP_PIXEL_FORMAT_TYPE(DstFormat);

	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;

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

	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (srcFlip != dstFlip)
		vFlip = TRUE;

	if (srcType != dstType)
		invert = TRUE;

	if (dstBytesPerPixel == 4)
	{
		if ((dstBitsPerPixel == 32) || (dstBitsPerPixel == 24))
		{
			UINT16* pSrcPixel;
			UINT32* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = ARGB32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = ARGB32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = ABGR32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = ABGR32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}
	else if (dstBytesPerPixel == 2)
	{
		if (dstBitsPerPixel == 16)
		{
			UINT16* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = RGB16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = RGB16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = BGR16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = BGR16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
		else if (dstBitsPerPixel == 15)
		{
			UINT16* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = RGB15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = RGB15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = BGR15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB15(r, g, b, *pSrcPixel);
							*pDstPixel = BGR15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}

	return -1;
}

int freerdp_image16_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int srcFlip;
	int dstFlip;
	int nSrcPad;
	int nDstPad;
	BYTE r, g, b;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	int srcType, dstType;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);
	srcFlip = FREERDP_PIXEL_FORMAT_FLIP(SrcFormat);
	srcType = FREERDP_PIXEL_FORMAT_TYPE(SrcFormat);

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);
	dstType = FREERDP_PIXEL_FORMAT_TYPE(DstFormat);

	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;

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

	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (srcFlip != dstFlip)
		vFlip = TRUE;

	if (srcType != dstType)
		invert = TRUE;

	if (dstBytesPerPixel == 4)
	{
		if ((dstBitsPerPixel == 32) || (dstBitsPerPixel == 24))
		{
			UINT16* pSrcPixel;
			UINT32* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = ARGB32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = ARGB32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = ABGR32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = ABGR32(0xFF, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}
	else if (dstBytesPerPixel == 2)
	{
		if (dstBitsPerPixel == 16)
		{
			UINT16* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						CopyMemory(pDstPixel, pSrcPixel, nWidth * 2);
						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcStep];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstStep];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						CopyMemory(pDstPixel, pSrcPixel, nWidth * 2);
						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-nSrcStep];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstStep];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = BGR16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = BGR16(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
		else if (dstBitsPerPixel == 15)
		{
			UINT16* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = RGB15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = RGB15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = (UINT16*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = BGR15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = (UINT16*) &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 2)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetRGB16(r, g, b, *pSrcPixel);
							*pDstPixel = BGR15(r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT16*) &((BYTE*) pSrcPixel)[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}

	return -1;
}

int freerdp_image24_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int srcFlip;
	int dstFlip;
	int nSrcPad;
	int nDstPad;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	int srcType, dstType;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);
	srcFlip = FREERDP_PIXEL_FORMAT_FLIP(SrcFormat);
	srcType = FREERDP_PIXEL_FORMAT_TYPE(SrcFormat);

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);
	dstType = FREERDP_PIXEL_FORMAT_TYPE(DstFormat);

	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;

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

	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (srcFlip != dstFlip)
		vFlip = TRUE;

	if (srcType != dstType)
		invert = TRUE;

	if (dstBytesPerPixel == 4)
	{
		if ((dstBitsPerPixel == 32) || (dstBitsPerPixel == 24))
		{
			BYTE* pSrcPixel;
			BYTE* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = 0xFF;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &pDstPixel[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = *pSrcPixel++;
							*pDstPixel++ = 0xFF;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &pDstPixel[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pDstPixel[0] = pSrcPixel[2];
							pDstPixel[1] = pSrcPixel[1];
							pDstPixel[2] = pSrcPixel[0];
							pDstPixel[3] = 0xFF;

							pSrcPixel += 3;
							pDstPixel += 4;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &pDstPixel[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							pDstPixel[0] = pSrcPixel[2];
							pDstPixel[1] = pSrcPixel[1];
							pDstPixel[2] = pSrcPixel[0];
							pDstPixel[3] = 0xFF;

							pSrcPixel += 3;
							pDstPixel += 4;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &pDstPixel[nDstPad];
					}
				}
			}

			return 1;
		}
	}
	else if (dstBytesPerPixel == 3)
	{
		BYTE* pSrcPixel;
		BYTE* pDstPixel;

		if (!invert)
		{
			if (!vFlip)
			{
				pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
				pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 3)];

				for (y = 0; y < nHeight; y++)
				{
					CopyMemory(pDstPixel, pSrcPixel, nWidth * 3);
					pSrcPixel = &pSrcPixel[nSrcStep];
					pDstPixel = &pDstPixel[nDstStep];
				}
			}
			else
			{
				pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
				pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 3)];

				for (y = 0; y < nHeight; y++)
				{
					CopyMemory(pDstPixel, pSrcPixel, nWidth * 3);
					pSrcPixel = &pSrcPixel[-nSrcStep];
					pDstPixel = &pDstPixel[nDstStep];
				}
			}
		}
		else
		{
			if (!vFlip)
			{
				pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
				pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 3)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth; x++)
					{
						pDstPixel[0] = pSrcPixel[2];
						pDstPixel[1] = pSrcPixel[1];
						pDstPixel[2] = pSrcPixel[0];

						pSrcPixel += 3;
						pDstPixel += 3;
					}

					pSrcPixel = &pSrcPixel[nSrcPad];
					pDstPixel = &pDstPixel[nDstPad];
				}
			}
			else
			{
				pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
				pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 3)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth; x++)
					{
						pDstPixel[0] = pSrcPixel[2];
						pDstPixel[1] = pSrcPixel[1];
						pDstPixel[2] = pSrcPixel[0];

						pSrcPixel += 3;
						pDstPixel += 3;
					}

					pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
					pDstPixel = &pDstPixel[nDstPad];
				}
			}
		}

		return 1;
	}
	else if (dstBytesPerPixel == 2)
	{
		if (dstBitsPerPixel == 16)
		{
			BYTE* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB16(pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB16(pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB16(pSrcPixel[0], pSrcPixel[1], pSrcPixel[2]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB16(pSrcPixel[0], pSrcPixel[1], pSrcPixel[2]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
		else if (dstBitsPerPixel == 15)
		{
			BYTE* pSrcPixel;
			UINT16* pDstPixel;

			if (!invert)
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB15(pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB15(pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
			}
			else
			{
				if (!vFlip)
				{
					pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB15(pSrcPixel[0], pSrcPixel[1], pSrcPixel[2]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[nSrcPad];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 3)];
					pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = RGB15(pSrcPixel[0], pSrcPixel[1], pSrcPixel[2]);
							pSrcPixel += 3;
						}

						pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
						pDstPixel = &((UINT16*) pDstPixel)[nDstPad];
					}
				}
			}

			return 1;
		}
	}

	return -1;
}

int freerdp_image32_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int x, y;
	int srcFlip;
	int dstFlip;
	int nSrcPad;
	int nDstPad;
	BYTE a, r, g, b;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	int srcType, dstType;
	BOOL vFlip = FALSE;
	BOOL invert = FALSE;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);
	srcFlip = FREERDP_PIXEL_FORMAT_FLIP(SrcFormat);
	srcType = FREERDP_PIXEL_FORMAT_TYPE(SrcFormat);

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	dstFlip = FREERDP_PIXEL_FORMAT_FLIP(DstFormat);
	dstType = FREERDP_PIXEL_FORMAT_TYPE(DstFormat);

	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;

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

	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));

	if (srcFlip != dstFlip)
		vFlip = TRUE;

	if (srcType != dstType)
		invert = TRUE;

	if (srcBitsPerPixel == 24)
	{
		if (dstBytesPerPixel == 4) /* srcBytesPerPixel == dstBytesPerPixel */
		{
			if (dstBitsPerPixel == 32)
			{
				UINT32* pSrcPixel;
				UINT32* pDstPixel;

				pSrcPixel = (UINT32*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
				pDstPixel = (UINT32*) &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

				if (!invert)
				{
					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							*pDstPixel++ = *pSrcPixel++;
						}

						pSrcPixel = (UINT32*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}
				else
				{
					for (y = 0; y < nHeight; y++)
					{
						for (x = 0; x < nWidth; x++)
						{
							GetARGB32(a, r, g, b, *pSrcPixel);
							*pDstPixel = ABGR32(a, r, g, b);

							pSrcPixel++;
							pDstPixel++;
						}

						pSrcPixel = (UINT32*) &((BYTE*) pSrcPixel)[nSrcPad];
						pDstPixel = (UINT32*) &((BYTE*) pDstPixel)[nDstPad];
					}
				}

				return 1;
			}
			else if (dstBitsPerPixel == 24) /* srcBitsPerPixel == dstBitsPerPixel */
			{
				BYTE* pSrcPixel;
				BYTE* pDstPixel;

				if (!invert)
				{
					if (!vFlip)
					{
						pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
						pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

						for (y = 0; y < nHeight; y++)
						{
							MoveMemory(pDstPixel, pSrcPixel, nWidth * 4);
							pSrcPixel = &pSrcPixel[nSrcStep];
							pDstPixel = &pDstPixel[nDstStep];
						}
					}
					else
					{
						pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 4)];
						pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

						for (y = 0; y < nHeight; y++)
						{
							MoveMemory(pDstPixel, pSrcPixel, nWidth * 4);
							pSrcPixel = &pSrcPixel[-nSrcStep];
							pDstPixel = &pDstPixel[nDstStep];
						}
					}
				}
				else
				{
					if (!vFlip)
					{
						pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
						pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

						for (y = 0; y < nHeight; y++)
						{
							for (x = 0; x < nWidth; x++)
							{
								pDstPixel[0] = pSrcPixel[2];
								pDstPixel[1] = pSrcPixel[1];
								pDstPixel[2] = pSrcPixel[0];
								pDstPixel[3] = 0xFF;

								pSrcPixel += 4;
								pDstPixel += 4;
							}

							pSrcPixel = &pSrcPixel[nSrcPad];
							pDstPixel = &pDstPixel[nDstPad];
						}
					}
					else
					{
						pSrcPixel = &pSrcData[((nYSrc + nHeight - 1) * nSrcStep) + (nXSrc * 4)];
						pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];

						for (y = 0; y < nHeight; y++)
						{
							for (x = 0; x < nWidth; x++)
							{
								pDstPixel[0] = pSrcPixel[2];
								pDstPixel[1] = pSrcPixel[1];
								pDstPixel[2] = pSrcPixel[0];
								pDstPixel[3] = 0xFF;

								pSrcPixel += 4;
								pDstPixel += 4;
							}

							pSrcPixel = &pSrcPixel[-((nSrcStep - nSrcPad) + nSrcStep)];
							pDstPixel = &pDstPixel[nDstPad];
						}
					}
				}

				return 1;
			}
		}
		else if (dstBytesPerPixel == 3)
		{
			UINT32* pSrcPixel;
			BYTE* pDstPixel;

			pSrcPixel = (UINT32*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
			pDstPixel = (BYTE*) &pDstData[(nYDst * nDstStep) + (nXDst * 3)];

			for (y = 0; y < nHeight; y++)
			{
				for (x = 0; x < nWidth; x++)
				{
					GetRGB32(r, g, b, *pSrcPixel);

					*pDstPixel++ = r;
					*pDstPixel++ = g;
					*pDstPixel++ = b;

					pSrcPixel++;
				}

				pSrcPixel = (UINT32*) &((BYTE*) pSrcPixel)[nSrcStep];
				pDstPixel = (BYTE*) &((BYTE*) pDstPixel)[nDstStep];
			}

			return 1;
		}
		else if (dstBytesPerPixel == 2)
		{
			if (dstBitsPerPixel == 16)
			{
				UINT32* pSrcPixel;
				UINT16* pDstPixel;

				pSrcPixel = (UINT32*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
				pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth; x++)
					{
						GetRGB32(r, g, b, *pSrcPixel);
						RGB_888_565(r, g, b);
						*pDstPixel = RGB565(r, g, b);

						pSrcPixel++;
						pDstPixel++;
					}

					pSrcPixel = (UINT32*) &((BYTE*) pSrcPixel)[nSrcPad];
					pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
				}

				return 1;
			}
			else if (dstBitsPerPixel == 15)
			{
				UINT32* pSrcPixel;
				UINT16* pDstPixel;

				pSrcPixel = (UINT32*) &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
				pDstPixel = (UINT16*) &pDstData[(nYDst * nDstStep) + (nXDst * 2)];

				for (y = 0; y < nHeight; y++)
				{
					for (x = 0; x < nWidth; x++)
					{
						GetRGB32(r, g, b, *pSrcPixel);
						RGB_888_555(r, g, b);
						*pDstPixel = RGB555(r, g, b);

						pSrcPixel++;
						pDstPixel++;
					}

					pSrcPixel = (UINT32*) &((BYTE*) pSrcPixel)[nSrcPad];
					pDstPixel = (UINT16*) &((BYTE*) pDstPixel)[nDstPad];
				}

				return 1;
			}
		}
	}

	return -1;
}

int freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, BYTE* pSrcData, DWORD SrcFormat, int nSrcStep, int nXSrc, int nYSrc, BYTE* palette)
{
	int status = -1;
	int srcBitsPerPixel;
	int srcBytesPerPixel;

	srcBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(SrcFormat);
	srcBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(SrcFormat) / 8);

	if (srcBytesPerPixel == 4)
	{
		status = freerdp_image32_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
				nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
	}
	else if (srcBytesPerPixel == 3)
	{
		status = freerdp_image24_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
				nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
	}
	else if (srcBytesPerPixel == 2)
	{
		if (srcBitsPerPixel == 16)
		{
			status = freerdp_image16_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
					nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
		}
		else if (srcBitsPerPixel == 15)
		{
			status = freerdp_image15_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
					nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
		}
	}
	else if (srcBytesPerPixel == 1)
	{
		status = freerdp_image8_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
				nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
	}
	else if (srcBitsPerPixel == 1)
	{
		status = freerdp_image1_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
				nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
	}
	else if (srcBitsPerPixel == 4)
	{
		status = freerdp_image4_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst,
				nWidth, nHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette);
	}

	if (status < 0)
	{
		int dstBitsPerPixel;
		int dstBytesPerPixel;

		dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
		dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);

		WLog_ERR(TAG, "failure: src: %d/%d dst: %d/%d",
				srcBitsPerPixel, srcBytesPerPixel, dstBitsPerPixel, dstBytesPerPixel);
	}

	return status;
}

int freerdp_image_move(BYTE* pData, DWORD Format, int nStep, int nXDst, int nYDst, int nWidth, int nHeight, int nXSrc, int nYSrc)
{
	int y;
	BOOL overlap;
	BYTE* pSrcPixel;
	BYTE* pDstPixel;
	int bytesPerPixel;

	bytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(Format) / 8);

	if (nStep < 0)
		nStep = nWidth * bytesPerPixel;

	overlap = (((nXDst + nWidth) > nXSrc) && (nXDst < (nXSrc + nWidth)) &&
		((nYDst + nHeight) > nYSrc) && (nYDst < (nYSrc + nHeight))) ? TRUE : FALSE;

	if (!overlap)
	{
		pSrcPixel = &pData[(nYSrc * nStep) + (nXSrc * bytesPerPixel)];
		pDstPixel = &pData[(nYDst * nStep) + (nXDst * bytesPerPixel)];

		for (y = 0; y < nHeight; y++)
		{
			CopyMemory(pDstPixel, pSrcPixel, nWidth * bytesPerPixel);
			pSrcPixel += nStep;
			pDstPixel += nStep;
		}

		return 1;
	}

	if (nYSrc < nYDst)
	{
		/* copy down */

		pSrcPixel = &pData[((nYSrc + nHeight - 1) * nStep) + (nXSrc * bytesPerPixel)];
		pDstPixel = &pData[((nYDst + nHeight - 1) * nStep) + (nXDst * bytesPerPixel)];

		for (y = 0; y < nHeight; y++)
		{
			CopyMemory(pDstPixel, pSrcPixel, nWidth * bytesPerPixel);
			pSrcPixel -= nStep;
			pDstPixel -= nStep;
		}
	}
	else if (nYSrc > nYDst)
	{
		/* copy up */

		pSrcPixel = &pData[(nYSrc * nStep) + (nXSrc * bytesPerPixel)];
		pDstPixel = &pData[(nYDst * nStep) + (nXDst * bytesPerPixel)];

		for (y = 0; y < nHeight; y++)
		{
			CopyMemory(pDstPixel, pSrcPixel, nWidth * bytesPerPixel);
			pSrcPixel += nStep;
			pDstPixel += nStep;
		}
	}
	else if (nXSrc > nXDst)
	{
		/* copy left */

		pSrcPixel = &pData[(nYSrc * nStep) + (nXSrc * bytesPerPixel)];
		pDstPixel = &pData[(nYDst * nStep) + (nXDst * bytesPerPixel)];

		for (y = 0; y < nHeight; y++)
		{
			MoveMemory(pDstPixel, pSrcPixel, nWidth * bytesPerPixel);
			pSrcPixel += nStep;
			pDstPixel += nStep;
		}
	}
	else
	{
		/* copy right */

		pSrcPixel = &pData[(nYSrc * nStep) + (nXSrc * bytesPerPixel)];
		pDstPixel = &pData[(nYDst * nStep) + (nXDst * bytesPerPixel)];

		for (y = 0; y < nHeight; y++)
		{
			MoveMemory(pDstPixel, pSrcPixel, nWidth * bytesPerPixel);
			pSrcPixel += nStep;
			pDstPixel += nStep;
		}
	}

	return 1;
}

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

	return (void*) ptr;
}

int freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
		int nWidth, int nHeight, UINT32 color)
{
	int y;
	int dstBitsPerPixel;
	int dstBytesPerPixel;

	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);

	if (dstBytesPerPixel == 4)
	{
		UINT32* pDstPixel;

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

		for (y = 0; y < nHeight; y++)
		{
			pDstPixel = (UINT32*) &pDstData[((nYDst + y) * nDstStep) + (nXDst * dstBytesPerPixel)];
			freerdp_image_memset32(pDstPixel, color, nWidth);
		}
	}
	else if (dstBytesPerPixel == 3)
	{

	}
	else if (dstBytesPerPixel == 2)
	{
		if (dstBitsPerPixel == 16)
		{

		}
		else if (dstBitsPerPixel == 15)
		{

		}
	}

	return 0;
}

int freerdp_image_copy_from_retina(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst, int nYDst,
				   int nWidth, int nHeight, BYTE* pSrcData, int nSrcStep, int nXSrc, int nYSrc)
{
	int x, y;
	int nSrcPad;
	int nDstPad;
	int srcBitsPerPixel;
	int srcBytesPerPixel;
	int dstBitsPerPixel;
	int dstBytesPerPixel;
	
	srcBitsPerPixel = 24;
	srcBytesPerPixel = 8;
	
	if (nSrcStep < 0)
		nSrcStep = srcBytesPerPixel * nWidth;
	
	dstBitsPerPixel = FREERDP_PIXEL_FORMAT_DEPTH(DstFormat);
	dstBytesPerPixel = (FREERDP_PIXEL_FORMAT_BPP(DstFormat) / 8);
	
	if (nDstStep < 0)
		nDstStep = dstBytesPerPixel * nWidth;
	
	nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
	nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
	
	if (dstBytesPerPixel == 4)
	{
		UINT32 R, G, B;
		BYTE* pSrcPixel;
		BYTE* pDstPixel;
		
		pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
		pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
		
		for (y = 0; y < nHeight; y++)
		{
			for (x = 0; x < nWidth; x++)
			{
				/* simple box filter scaling, could be improved with better algorithm */
				
				B = pSrcPixel[0] + pSrcPixel[4] + pSrcPixel[nSrcStep + 0] + pSrcPixel[nSrcStep + 4];
				G = pSrcPixel[1] + pSrcPixel[5] + pSrcPixel[nSrcStep + 1] + pSrcPixel[nSrcStep + 5];
				R = pSrcPixel[2] + pSrcPixel[6] + pSrcPixel[nSrcStep + 2] + pSrcPixel[nSrcStep + 6];
				pSrcPixel += 8;
				
				*pDstPixel++ = (BYTE) (B >> 2);
				*pDstPixel++ = (BYTE) (G >> 2);
				*pDstPixel++ = (BYTE) (R >> 2);
				*pDstPixel++ = 0xFF;
			}
			
			pSrcPixel = &pSrcPixel[nSrcPad + nSrcStep];
			pDstPixel = &pDstPixel[nDstPad];
		}
	}
	
	return 1;
}
