#include "unittest/TestSuite.h"
#include "bitmapimage/BitmapImage.h"
#include <stdio.h>
#include <string.h>

static void printHexColorSingle(char * outString, uint32_t color, unsigned int componentCount) {
	for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
		sprintf(outString + componentIndex * 2, "%02X", color >> componentIndex * 8 | 0xFF);
	}
}

static void assertPixelsClear(BitmapImage * image, uint32_t color, unsigned int line) {
	unsigned int componentCount = BitmapImage_pixelFormatBytes(image->pixelFormat);
	for (unsigned int pixelIndex = 0; pixelIndex < image->width * image->height; pixelIndex++) {
		for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
			if (image->pixels[pixelIndex * componentCount + componentIndex] != (color >> (8 * (componentCount - 1 - componentIndex)) & 0xFF)) {
				char expectedColorString[1 + componentCount * 2], actualColorString[1 + componentCount * 2];
				uint32_t actualColor = 0;
				for (unsigned int componentIndex2 = 0; componentIndex2 < componentCount; componentIndex2++) {
					actualColor <<= 8;
					actualColor |= image->pixels[pixelIndex * componentCount + componentIndex];
				}
				printHexColorSingle(expectedColorString, color, componentCount);
				printHexColorSingle(actualColorString, actualColor, componentCount);
				TestCase_assert(false, "Pixel mismatch on line %u, at %u, %u; expected all to be %s, but found %s", line, pixelIndex / image->width, pixelIndex % image->width, expectedColorString, actualColorString);
			}
		}
	}
	TestCase_assert(true, NULL);
}

static void printPixels(const unsigned char * pixels, unsigned int width, unsigned int height, unsigned int componentCount) {
	for (unsigned int rowIndex = 0; rowIndex < height; rowIndex++) {
		fprintf(stderr, "  ");
		for (unsigned int columnIndex = 0; columnIndex < width; columnIndex++) {
			for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
				fprintf(stderr, "%02X", pixels[(rowIndex * width + columnIndex) * componentCount + componentIndex]);
			}
			fputc(' ', stderr);
		}
		fputc('\n', stderr);
	}
}

static void assertPixelsEqual(BitmapImage * image, const unsigned char * expectedPixels, unsigned int line) {
	unsigned int width = image->width;
	unsigned int height = image->height;
	unsigned int componentCount = BitmapImage_pixelFormatBytes(image->pixelFormat);
	for (unsigned int rowIndex = 0; rowIndex < height; rowIndex++) {
		for (unsigned int columnIndex = 0; columnIndex < width; columnIndex++) {
			for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
				if (image->pixels[(rowIndex * width + columnIndex) * componentCount + componentIndex] != expectedPixels[(rowIndex * width + columnIndex) * componentCount + componentIndex]) {
					fprintf(stderr, "Expected image:\n");
					printPixels(expectedPixels, width, height, componentCount);
					fprintf(stderr, "Actual image:\n");
					printPixels(image->pixels, width, height, componentCount);
					TestCase_assert(false, "Image data mismatch at %u, %u (line %u)", columnIndex, rowIndex, line);
					return;
				}
			}
		}
	}
	TestCase_assert(true, NULL);
}

static void testInit(void) {
	BitmapImage * bitmapImagePtr, bitmapImage;
	bool success;
	unsigned char pixelsGray2x2[] = {
		0xFF, 0xBF,
		0x7F, 0x3F
	};
	unsigned char pixelsGrayAlpha2x2[] = {
		0xFF,0xFF, 0xBF,0x7F,
		0x7F,0x00, 0x3F,0xFF
	};
	unsigned char pixelsRGB3x2[] = {
		0xFF,0x00,0x00, 0x00,0xFF,0x00, 0x00,0x00,0xFF,
		0xFF,0xFF,0x00, 0xFF,0x00,0xFF, 0x00,0xFF,0xFF
	};
	unsigned char pixelsRGBA2x3[] = {
		0xFF,0x00,0x00,0x7F, 0x00,0xFF,0x00,0xBF,
		0x00,0x00,0x00,0xFF, 0xFF,0xFF,0xFF,0x00,
		0xFF,0xFF,0x00,0xFF, 0x00,0x00,0xFF,0x3F
	};
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_init(&bitmapImage, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_GRAY_8);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_init(&bitmapImage, BITMAP_PIXEL_FORMAT_RGB_888, 3, 2);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_RGB_888);
	TestCase_assertUIntEqual(bitmapImage.width, 3);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixels(&bitmapImage, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixelsGray2x2);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_GRAY_8);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsGray2x2);
	assertPixelsEqual(&bitmapImage, pixelsGray2x2, __LINE__);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixels(&bitmapImage, BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 2, pixelsGrayAlpha2x2);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsGrayAlpha2x2);
	assertPixelsEqual(&bitmapImage, pixelsGrayAlpha2x2, __LINE__);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixels(&bitmapImage, BITMAP_PIXEL_FORMAT_RGB_888, 3, 2, pixelsRGB3x2);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_RGB_888);
	TestCase_assertUIntEqual(bitmapImage.width, 3);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsRGB3x2);
	assertPixelsEqual(&bitmapImage, pixelsRGB3x2, __LINE__);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixels(&bitmapImage, BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixelsRGBA2x3);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 3);
	TestCase_assertPointerNonNULL(bitmapImage.pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsRGBA2x3);
	assertPixelsEqual(&bitmapImage, pixelsRGBA2x3, __LINE__);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixelsNoCopy(&bitmapImage, BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 2, pixelsGrayAlpha2x2, false);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 2);
	TestCase_assertPointerEqual(bitmapImage.pixels, pixelsGrayAlpha2x2);
	BitmapImage_dispose(&bitmapImage);
	
	stemobject_assign_vtable(bitmapImage, BitmapImage);
	success = BitmapImage_initWithPixelsNoCopy(&bitmapImage, BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixelsRGBA2x3, false);
	TestCase_assertBoolTrue(success);
	TestCase_assertIntEqual(bitmapImage.pixelFormat, BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertUIntEqual(bitmapImage.width, 2);
	TestCase_assertUIntEqual(bitmapImage.height, 3);
	TestCase_assertPointerEqual(bitmapImage.pixels, pixelsRGBA2x3);
	BitmapImage_dispose(&bitmapImage);
	
	bitmapImagePtr = BitmapImage_create(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_GRAY_8);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_create(BITMAP_PIXEL_FORMAT_RGB_888, 3, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_RGB_888);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 3);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixels(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixelsGray2x2);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_GRAY_8);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsGray2x2);
	assertPixelsEqual(bitmapImagePtr, pixelsGray2x2, __LINE__);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixels(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 2, pixelsGrayAlpha2x2);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	TestCase_assertPointerUnequal(bitmapImage.pixels, pixelsGrayAlpha2x2);
	assertPixelsEqual(bitmapImagePtr, pixelsGrayAlpha2x2, __LINE__);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixels(BITMAP_PIXEL_FORMAT_RGB_888, 3, 2, pixelsRGB3x2);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_RGB_888);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 3);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	TestCase_assertPointerUnequal(bitmapImagePtr->pixels, pixelsRGB3x2);
	assertPixelsEqual(bitmapImagePtr, pixelsRGB3x2, __LINE__);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixels(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixelsRGBA2x3);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 3);
	TestCase_assertPointerNonNULL(bitmapImagePtr->pixels);
	TestCase_assertPointerUnequal(bitmapImagePtr->pixels, pixelsRGBA2x3);
	assertPixelsEqual(bitmapImagePtr, pixelsRGBA2x3, __LINE__);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 2, pixelsGrayAlpha2x2, false);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 2);
	TestCase_assertPointerEqual(bitmapImagePtr->pixels, pixelsGrayAlpha2x2);
	BitmapImage_dispose(bitmapImagePtr);
	
	bitmapImagePtr = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixelsRGBA2x3, false);
	TestCase_assertPointerNonNULL(bitmapImagePtr);
	TestCase_assertIntEqual(bitmapImagePtr->pixelFormat, BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertUIntEqual(bitmapImagePtr->width, 2);
	TestCase_assertUIntEqual(bitmapImagePtr->height, 3);
	TestCase_assertPointerEqual(bitmapImagePtr->pixels, pixelsRGBA2x3);
	BitmapImage_dispose(bitmapImagePtr);
}

static void testClear(void) {
	BitmapImage * image;
	
	image = BitmapImage_create(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2);
	BitmapImage_clear(image, 0xFFBF7F3F);
	assertPixelsClear(image, 0xFFBF7F3F, __LINE__);
	BitmapImage_clear(image, 0x00112233);
	assertPixelsClear(image, 0x00112233, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_create(BITMAP_PIXEL_FORMAT_RGB_888, 3, 2);
	BitmapImage_clear(image, 0xFF7700);
	assertPixelsClear(image, 0xFF7700, __LINE__);
	BitmapImage_clear(image, 0x112233);
	assertPixelsClear(image, 0x112233, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_create(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 3);
	BitmapImage_clear(image, 0xFF77);
	assertPixelsClear(image, 0xFF77, __LINE__);
	BitmapImage_clear(image, 0x0011);
	assertPixelsClear(image, 0x0011, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_create(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3);
	BitmapImage_clear(image, 0xFF);
	assertPixelsClear(image, 0xFF, __LINE__);
	BitmapImage_clear(image, 0x00);
	assertPixelsClear(image, 0x00, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithClearColor(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, 0xFFBF7F3F);
	assertPixelsClear(image, 0xFFBF7F3F, __LINE__);
	BitmapImage_dispose(image);
	image = BitmapImage_createWithClearColor(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, 0x00112233);
	assertPixelsClear(image, 0x00112233, __LINE__);
	BitmapImage_dispose(image);
}

static void testCopyPixels(void) {
	unsigned char sourcePixels1[] = {
		0x01, 0x02, 0x03,
		0x04, 0x05, 0x06,
		0x07, 0x08, 0x09
	};
	unsigned char expectedPixels1[] = {
		0x00, 0x00, 0x00,
		0x00, 0x00, 0x00,
		0x00, 0x01, 0x02
	};
	unsigned char expectedPixels2[] = {
		0x05, 0x06, 0x00,
		0x08, 0x09, 0x00,
		0x00, 0x01, 0x02
	};
	unsigned char expectedPixels3[] = {
		0x01, 0x02, 0x00,
		0x04, 0x05, 0x00,
		0x00, 0x00, 0x00
	};
	unsigned char expectedPixels4[] = {
		0x00, 0x00, 0x00,
		0x02, 0x03, 0x00,
		0x05, 0x06, 0x00
	};
	unsigned char expectedPixels5[] = {
		0x00, 0x00, 0x00,
		0x00, 0x01, 0x00,
		0x00, 0x00, 0x00
	};
	
	BitmapImage * source = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, sourcePixels1, false);
	BitmapImage * target = BitmapImage_create(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3);
	BitmapImage_clear(target, 0);
	
	BitmapImage_copyPixels(target, source, 0, 0, 0, 0, 3, 3);
	assertPixelsEqual(target, sourcePixels1, __LINE__);
	
	BitmapImage_clear(target, 0);
	BitmapImage_copyPixels(target, source, 0, 0, 1, 2, 3, 3);
	assertPixelsEqual(target, expectedPixels1, __LINE__);
	
	BitmapImage_copyPixels(target, source, 1, 1, 0, 0, 3, 3);
	assertPixelsEqual(target, expectedPixels2, __LINE__);
	
	BitmapImage_clear(target, 0);
	BitmapImage_copyPixels(target, source, 0, 0, 0, 0, 2, 2);
	assertPixelsEqual(target, expectedPixels3, __LINE__);
	
	BitmapImage_clear(target, 0);
	BitmapImage_copyPixels(target, source, 0, 0, -1, 1, 3, 3);
	assertPixelsEqual(target, expectedPixels4, __LINE__);
	
	BitmapImage_clear(target, 0);
	BitmapImage_copyPixels(target, source, -1, -1, 0, 0, 2, 2);
	assertPixelsEqual(target, expectedPixels5, __LINE__);
	
	BitmapImage_dispose(target);
	BitmapImage_dispose(source);
}

static void testComposite(void) {
	BitmapImage * source1, * source2, * target;
	unsigned char source1Pixels[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0xFF,0x00,0xFF,
		0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
	};
	unsigned char source2Pixels[] = {
		0x00,0x00,0x00,0x7F, 0xFF,0xFF,0xFF,0x7F,
		0x00,0x00,0x00,0x3F, 0xFF,0xFF,0xFF,0x3F
	};
	unsigned char expectedPixels1[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0x00,0x00,0x00,
		0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
	};
	unsigned char expectedPixels2[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0x00,0x00,0x00,
		0x00,0x00,0xFF,0xFF, 0xFF,0x00,0x00,0xFF, 0x00,0xFF,0x00,0xFF,
		0x00,0x00,0x00,0x00, 0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
	};
	unsigned char expectedPixels3[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0x80,0x00,0xFF, 0xFF,0xFF,0xFF,0x7F,
		0x00,0x00,0xFF,0xFF, 0xC0,0x00,0x00,0xFF, 0x3E,0xFF,0x3E,0xFF,
		0x00,0x00,0x00,0x00, 0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
	};
	unsigned char expectedPixels4[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0x80,0x00,0xFF, 0xFF,0xFF,0xFF,0x7F,
		0x00,0xFF,0x00,0xFF, 0xC0,0x00,0x00,0xFF, 0x3E,0xFF,0x3E,0xFF,
		0xFF,0xFF,0xFF,0xFF, 0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
	};
	unsigned char expectedPixels5[] = {
		0xFF,0x00,0x00,0xFF, 0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
		0x00,0xFF,0x00,0xFF, 0xC0,0x00,0x00,0xFF, 0x3E,0xFF,0x3E,0xFF,
		0xFF,0xFF,0xFF,0xFF, 0x00,0x00,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
	};
	
	source1 = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, source1Pixels, false);
	source2 = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, source2Pixels, false);
	target = BitmapImage_create(BITMAP_PIXEL_FORMAT_RGBA_8888, 3, 3);
	
	BitmapImage_clear(target, 0x00000000);
	BitmapImage_composite(target, source1, 0, 0, 0, 0, 2, 2);
	assertPixelsEqual(target, expectedPixels1, __LINE__);
	
	BitmapImage_composite(target, source1, 0, 0, 1, 1, 2, 2);
	assertPixelsEqual(target, expectedPixels2, __LINE__);
	
	BitmapImage_composite(target, source2, 0, 0, 1, 0, 2, 2);
	assertPixelsEqual(target, expectedPixels3, __LINE__);
	
	BitmapImage_composite(target, source1, 1, 0, 0, 1, 1, 2);
	assertPixelsEqual(target, expectedPixels4, __LINE__);
	
	BitmapImage_composite(target, source1, 0, 1, 1, 0, 2, 1);
	assertPixelsEqual(target, expectedPixels5, __LINE__);
	
	BitmapImage_dispose(source1);
	BitmapImage_dispose(source2);
	BitmapImage_dispose(target);
}

static void testPremultiply(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0xFF,0x00,0x00,0x7F, 0x00,0xFF,0x00,0x3F,
		0x00,0x00,0xFF,0x1F, 0xFF,0xFF,0xFF,0x00
	};
	unsigned char pixels2[] = {
		0xFF,0x00,0x00,0xFF, 0x7F,0xFF,0x7F,0x7F,
		0x20,0x40,0x80,0x7F, 0x00,0x00,0x00,0x00
	};
	unsigned char expectedPixels1[] = {
		0x7F,0x00,0x00,0x7F, 0x00,0x3F,0x00,0x3F,
		0x00,0x00,0x1F,0x1F, 0x00,0x00,0x00,0x00
	};
	unsigned char expectedPixels2[] = {
		0xFF,0x00,0x00,0xFF, 0x3F,0x7F,0x3F,0x7F,
		0x0F,0x1F,0x3F,0x7F, 0x00,0x00,0x00,0x00
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, pixels1, false);
	BitmapImage_premultiply(image);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, pixels2, false);
	BitmapImage_premultiply(image);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testUnpremultiply(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x7F,0x00,0x00,0x7F, 0x00,0x3F,0x00,0x3F,
		0x00,0x00,0x1F,0x1F, 0x00,0x00,0x00,0x00
	};
	unsigned char pixels2[] = {
		0xFF,0x00,0x00,0xFF, 0x3F,0x7F,0x3F,0x7F,
		0x0F,0x1F,0x3F,0x7F, 0x00,0x00,0x00,0x00
	};
	unsigned char expectedPixels1[] = {
		0xFF,0x00,0x00,0x7F, 0x00,0xFF,0x00,0x3F,
		0x00,0x00,0xFF,0x1F, 0x00,0x00,0x00,0x00
	};
	unsigned char expectedPixels2[] = {
		0xFF,0x00,0x00,0xFF, 0x7E,0xFF,0x7E,0x7F,
		0x1E,0x3E,0x7E,0x7F, 0x00,0x00,0x00,0x00
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, pixels1, false);
	BitmapImage_unpremultiply(image);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, pixels2, false);
	BitmapImage_unpremultiply(image);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testFlipHorizontal(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,
		0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17
	};
	unsigned char pixels2[] = {
		0x00,0x02, 0x04,0x06, 0x08,0x0A, 0x0C,0x0E,
		0x10,0x12, 0x14,0x16, 0x18,0x1A, 0x1C,0x1E,
		0x20,0x22, 0x24,0x26, 0x28,0x2A, 0x2C,0x2E
	};
	unsigned char expectedPixels1[] = {
		0x08,0x09,0x0A,0x0B, 0x04,0x05,0x06,0x07, 0x00,0x01,0x02,0x03,
		0x14,0x15,0x16,0x17, 0x10,0x11,0x12,0x13, 0x0C,0x0D,0x0E,0x0F
	};
	unsigned char expectedPixels2[] = {
		0x0C,0x0E, 0x08,0x0A, 0x04,0x06, 0x00,0x02,
		0x1C,0x1E, 0x18,0x1A, 0x14,0x16, 0x10,0x12,
		0x2C,0x2E, 0x28,0x2A, 0x24,0x26, 0x20,0x22
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 3, 2, pixels1, false);
	BitmapImage_flipHorizontal(image);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 4, 3, pixels2, false);
	BitmapImage_flipHorizontal(image);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testFlipVertical(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
		0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F,
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17
	};
	unsigned char pixels2[] = {
		0x00,0x02, 0x04,0x06, 0x08,0x0A,
		0x0C,0x0E, 0x10,0x12, 0x14,0x16,
		0x18,0x1A, 0x1C,0x1E, 0x20,0x22,
		0x24,0x26, 0x28,0x2A, 0x2C,0x2E
	};
	unsigned char expectedPixels1[] = {
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
		0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F,
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07
	};
	unsigned char expectedPixels2[] = {
		0x24,0x26, 0x28,0x2A, 0x2C,0x2E,
		0x18,0x1A, 0x1C,0x1E, 0x20,0x22,
		0x0C,0x0E, 0x10,0x12, 0x14,0x16,
		0x00,0x02, 0x04,0x06, 0x08,0x0A
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixels1, false);
	BitmapImage_flipVertical(image);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 3, 4, pixels2, false);
	BitmapImage_flipVertical(image);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testRotate180(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
		0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F,
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17
	};
	unsigned char pixels2[] = {
		0x00,0x02, 0x04,0x06, 0x08,0x0A,
		0x0C,0x0E, 0x10,0x12, 0x14,0x16,
		0x18,0x1A, 0x1C,0x1E, 0x20,0x22,
		0x24,0x26, 0x28,0x2A, 0x2C,0x2E
	};
	unsigned char expectedPixels1[] = {
		0x14,0x15,0x16,0x17, 0x10,0x11,0x12,0x13,
		0x0C,0x0D,0x0E,0x0F, 0x08,0x09,0x0A,0x0B,
		0x04,0x05,0x06,0x07, 0x00,0x01,0x02,0x03
	};
	unsigned char expectedPixels2[] = {
		0x2C,0x2E, 0x28,0x2A, 0x24,0x26,
		0x20,0x22, 0x1C,0x1E, 0x18,0x1A,
		0x14,0x16, 0x10,0x12, 0x0C,0x0E,
		0x08,0x0A, 0x04,0x06, 0x00,0x02
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixels1, false);
	BitmapImage_rotate180(image);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 3, 4, pixels2, false);
	BitmapImage_rotate180(image);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testRotate90CW(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
		0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F,
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17
	};
	unsigned char pixels2[] = {
		0x00,0x02, 0x04,0x06, 0x08,0x0A,
		0x0C,0x0E, 0x10,0x12, 0x14,0x16,
		0x18,0x1A, 0x1C,0x1E, 0x20,0x22,
		0x24,0x26, 0x28,0x2A, 0x2C,0x2E
	};
	unsigned char expectedPixels1[] = {
		0x10,0x11,0x12,0x13, 0x08,0x09,0x0A,0x0B, 0x00,0x01,0x02,0x03,
		0x14,0x15,0x16,0x17, 0x0C,0x0D,0x0E,0x0F, 0x04,0x05,0x06,0x07
	};
	unsigned char expectedPixels2[] = {
		0x24,0x26, 0x18,0x1A, 0x0C,0x0E, 0x00,0x02,
		0x28,0x2A, 0x1C,0x1E, 0x10,0x12, 0x04,0x06,
		0x2C,0x2E, 0x20,0x22, 0x14,0x16, 0x08,0x0A
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixels1, false);
	BitmapImage_rotate90CW(image);
	TestCase_assertUIntEqual(image->width, 3);
	TestCase_assertUIntEqual(image->height, 2);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 3, 4, pixels2, false);
	BitmapImage_rotate90CW(image);
	TestCase_assertUIntEqual(image->width, 4);
	TestCase_assertUIntEqual(image->height, 3);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testRotate90CCW(void) {
	BitmapImage * image;
	unsigned char pixels1[] = {
		0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
		0x08,0x09,0x0A,0x0B, 0x0C,0x0D,0x0E,0x0F,
		0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17
	};
	unsigned char pixels2[] = {
		0x00,0x02, 0x04,0x06, 0x08,0x0A,
		0x0C,0x0E, 0x10,0x12, 0x14,0x16,
		0x18,0x1A, 0x1C,0x1E, 0x20,0x22,
		0x24,0x26, 0x28,0x2A, 0x2C,0x2E
	};
	unsigned char expectedPixels1[] = {
		0x04,0x05,0x06,0x07, 0x0C,0x0D,0x0E,0x0F, 0x14,0x15,0x16,0x17,
		0x00,0x01,0x02,0x03, 0x08,0x09,0x0A,0x0B, 0x10,0x11,0x12,0x13
	};
	unsigned char expectedPixels2[] = {
		0x08,0x0A, 0x14,0x16, 0x20,0x22, 0x2C,0x2E,
		0x04,0x06, 0x10,0x12, 0x1C,0x1E, 0x28,0x2A,
		0x00,0x02, 0x0C,0x0E, 0x18,0x1A, 0x24,0x26
	};
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 3, pixels1, false);
	BitmapImage_rotate90CCW(image);
	TestCase_assertUIntEqual(image->width, 3);
	TestCase_assertUIntEqual(image->height, 2);
	assertPixelsEqual(image, expectedPixels1, __LINE__);
	BitmapImage_dispose(image);
	
	image = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 3, 4, pixels2, false);
	BitmapImage_rotate90CCW(image);
	TestCase_assertUIntEqual(image->width, 4);
	TestCase_assertUIntEqual(image->height, 3);
	assertPixelsEqual(image, expectedPixels2, __LINE__);
	BitmapImage_dispose(image);
}

static void testCopyToPixelFormat(void) {
	unsigned char pixelsG[] = {
		0x00, 0x01,
		0x02, 0x03
	};
	unsigned char pixelsGA[] = {
		0x00,0x00, 0x01,0x3F,
		0x02,0x7F, 0x03,0xFF
	};
	unsigned char pixelsRGB[] = {
		0x00,0x10,0x40, 0x01,0x11,0x41,
		0x02,0x12,0x42, 0x03,0x13,0x43
	};
	unsigned char pixelsRGBA[] = {
		0x00,0x10,0x40,0x00, 0x01,0x11,0x41,0x3F,
		0x02,0x12,0x42,0x7F, 0x03,0x13,0x43,0xFF
	};
	unsigned char pixelsGA_alphaAdded[] = {
		0x00,0xFF, 0x01,0xFF,
		0x02,0xFF, 0x03,0xFF
	};
	unsigned char pixelsRGB_fromGray[] = {
		0x00,0x00,0x00, 0x01,0x01,0x01,
		0x02,0x02,0x02, 0x03,0x03,0x03
	};
	unsigned char pixelsRGBA_fromGray[] = {
		0x00,0x00,0x00,0x00, 0x01,0x01,0x01,0x3F,
		0x02,0x02,0x02,0x7F, 0x03,0x03,0x03,0xFF
	};
	unsigned char pixelsRGBA_fromGray_alphaAdded[] = {
		0x00,0x00,0x00,0xFF, 0x01,0x01,0x01,0xFF,
		0x02,0x02,0x02,0xFF, 0x03,0x03,0x03,0xFF
	};
	unsigned char pixelsRGBA_alphaAdded[] = {
		0x00,0x10,0x40,0xFF, 0x01,0x11,0x41,0xFF,
		0x02,0x12,0x42,0xFF, 0x03,0x13,0x43,0xFF
	};
	BitmapImage * sourceImageG = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixelsG, false);
	BitmapImage * sourceImageGA = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAYALPHA_88, 2, 2, pixelsGA, false);
	BitmapImage * sourceImageRGB = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGB_888, 2, 2, pixelsRGB, false);
	BitmapImage * sourceImageRGBA = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_RGBA_8888, 2, 2, pixelsRGBA, false);
	
	BitmapImage * targetImage = BitmapImage_copyToPixelFormat(sourceImageG, BITMAP_PIXEL_FORMAT_GRAY_8);
	assertPixelsEqual(targetImage, pixelsG, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageG, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	assertPixelsEqual(targetImage, pixelsGA_alphaAdded, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageG, BITMAP_PIXEL_FORMAT_RGB_888);
	assertPixelsEqual(targetImage, pixelsRGB_fromGray, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageG, BITMAP_PIXEL_FORMAT_RGBA_8888);
	assertPixelsEqual(targetImage, pixelsRGBA_fromGray_alphaAdded, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageGA, BITMAP_PIXEL_FORMAT_GRAY_8);
	assertPixelsEqual(targetImage, pixelsG, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageGA, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	assertPixelsEqual(targetImage, pixelsGA, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageGA, BITMAP_PIXEL_FORMAT_RGB_888);
	assertPixelsEqual(targetImage, pixelsRGB_fromGray, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageGA, BITMAP_PIXEL_FORMAT_RGBA_8888);
	assertPixelsEqual(targetImage, pixelsRGBA_fromGray, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGB, BITMAP_PIXEL_FORMAT_GRAY_8);
	assertPixelsEqual(targetImage, pixelsG, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGB, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	assertPixelsEqual(targetImage, pixelsGA_alphaAdded, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGB, BITMAP_PIXEL_FORMAT_RGB_888);
	assertPixelsEqual(targetImage, pixelsRGB, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGB, BITMAP_PIXEL_FORMAT_RGBA_8888);
	assertPixelsEqual(targetImage, pixelsRGBA_alphaAdded, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGBA, BITMAP_PIXEL_FORMAT_GRAY_8);
	assertPixelsEqual(targetImage, pixelsG, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGBA, BITMAP_PIXEL_FORMAT_GRAYALPHA_88);
	assertPixelsEqual(targetImage, pixelsGA, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGBA, BITMAP_PIXEL_FORMAT_RGB_888);
	assertPixelsEqual(targetImage, pixelsRGB, __LINE__);
	BitmapImage_dispose(targetImage);
	
	targetImage = BitmapImage_copyToPixelFormat(sourceImageRGBA, BITMAP_PIXEL_FORMAT_RGBA_8888);
	assertPixelsEqual(targetImage, pixelsRGBA, __LINE__);
	BitmapImage_dispose(targetImage);
	
	BitmapImage_dispose(sourceImageG);
	BitmapImage_dispose(sourceImageGA);
	BitmapImage_dispose(sourceImageRGB);
	BitmapImage_dispose(sourceImageRGBA);
}

TEST_SUITE(BitmapImageTest,
           testInit,
           testClear,
           testCopyPixels,
           testComposite,
           testPremultiply,
           testUnpremultiply,
           testFlipHorizontal,
           testFlipVertical,
           testRotate180,
           testRotate90CW,
           testRotate90CCW,
           testCopyToPixelFormat)
