int DLL_CALLCONV
FIA_ReplaceColourPlanes (FIBITMAP **src, FIBITMAP *R, FIBITMAP *G, FIBITMAP *B)
{

	if (FreeImage_HasPixels(R) && FIA_Is8Bit(R)) {
		if (*src==NULL)
			*src = FreeImage_Allocate (FreeImage_GetWidth(R), FreeImage_GetHeight(R), 24);
		FIA_SetGreyLevelPalette (R);
		FreeImage_SetChannel(*src, R, FICC_RED);
	}
	
	if (FreeImage_HasPixels(G) && FIA_Is8Bit(G)) {
		if (*src==NULL)
			*src = FreeImage_Allocate (FreeImage_GetWidth(G), FreeImage_GetHeight(G), 24);
		FIA_SetGreyLevelPalette (G);
		FreeImage_SetChannel(*src, G, FICC_GREEN);
	}
	
	if (FreeImage_HasPixels(B) && FIA_Is8Bit(B)) {
		if (*src==NULL)
			*src = FreeImage_Allocate (FreeImage_GetWidth(B), FreeImage_GetHeight(B), 24);
		FIA_SetGreyLevelPalette (B);
		FreeImage_SetChannel(*src, B, FICC_BLUE);
	}
		
	return FIA_SUCCESS;
}
static void
TestFIA_AffineTransorm8bitTest(CuTest* tc)
{
  const char *file = TEST_DATA_DIR "fly.bmp";

  FIBITMAP *dib1 = FIA_LoadFIBFromFile(file);

  CuAssertTrue(tc, dib1 != NULL);

  FIA_Matrix *matrix = FIA_MatrixNew();

  FIA_MatrixRotate(matrix, -45.0, FIA_MatrixOrderAppend);

  FIA_MatrixTranslate(matrix, 40, 40, FIA_MatrixOrderAppend);
  
  FIA_MatrixScale(matrix, 1.2, 1.2, FIA_MatrixOrderPrepend);
  
  FIBITMAP *transformed_dib = FIA_AffineTransform(dib1, FreeImage_GetWidth(dib1), FreeImage_GetHeight(dib1),
                                  matrix, FIA_RGBQUAD(255,0,0), 0);
  
  FIA_MatrixDestroy(matrix);
  
  CuAssertTrue(tc, transformed_dib != NULL);

  FIA_SetGreyLevelPalette(transformed_dib);

  FIA_SaveFIBToFile(transformed_dib, TEST_DATA_OUTPUT_DIR "Drawing/TestFIA_AffineTransorm8bitTest.bmp", BIT8);

  FreeImage_Unload(dib1);
  FreeImage_Unload(transformed_dib);
}
static void
TestFIA_AffineTransorm32bitScaleTest(CuTest* tc)
{
  const char *file = TEST_DATA_DIR "fly.bmp";

  FIBITMAP *dib1 = FIA_LoadFIBFromFile(file);

  CuAssertTrue(tc, dib1 != NULL);

  FIBITMAP *dib2 = FreeImage_ConvertTo32Bits(dib1);
  
  CuAssertTrue(tc, dib2 != NULL);
  
  FIA_Matrix *matrix = FIA_MatrixNew();

  FIA_MatrixScale(matrix, 2.0, 2.0, FIA_MatrixOrderPrepend);
  
  FIBITMAP *transformed_dib = FIA_AffineTransform(dib2, 1000, 1000,
                                matrix, FIA_RGBQUAD(255,0,255), 0);
  
  FIA_MatrixDestroy(matrix);
  
  CuAssertTrue(tc, transformed_dib != NULL);

  FIA_SetGreyLevelPalette(transformed_dib);

  FIA_SaveFIBToFile(transformed_dib, TEST_DATA_OUTPUT_DIR "Drawing/TestFIA_AffineTransorm32bitScaleTest.bmp", BIT24);

  FreeImage_Unload(dib1);
  FreeImage_Unload(dib2);
  FreeImage_Unload(transformed_dib);
}
static void
TestFIA_FloodFillTest(CuTest* tc)
{
    const char *file = TEST_DATA_DIR "particle.bmp";

    FIBITMAP *dib1 = FIA_LoadFIBFromFile(file);

    CuAssertTrue(tc, dib1 != NULL);

    FIBITMAP *dib2 = FreeImage_ConvertTo8Bits(dib1);

    CuAssertTrue(tc, dib2 != NULL);

    FIBITMAP *dib3 = FIA_FloodFill(dib2, 20, 1, 255);

    FIA_SetGreyLevelPalette(dib3);
    
    CuAssertTrue(tc, dib3 != NULL);

    FIA_SaveFIBToFile(dib3, TEST_DATA_OUTPUT_DIR "Drawing/TestFIA_FloodFillTest.jpg", BIT24);

    FreeImage_Unload(dib1);
    FreeImage_Unload(dib2);
    FreeImage_Unload(dib3);
}
/*
static inline int __cdecl
ComparePoints (const void *element1, const void *element2)
{
    FIAPOINT point1 = *(FIAPOINT *) element1;
    FIAPOINT point2 = *(FIAPOINT *) element2;

    if (point1.x > point2.x)
    {
        return 1;
    }
    else if (point1.x < point2.x)
    {
        return -1;
    }
    else
    {
        // Same x sort by y
        if (point1.y > point2.y)
        {
            return 1;
        }
        else if (point1.y < point2.y)
        {
            return -1;
        }
    }

    return 0;
}
*/
FIBITMAP *DLL_CALLCONV
FreeImage_ConvexHull (FIBITMAP * src)
{
	FIBITMAP *tmp = FIA_BinaryInnerBorder(src);
	FIA_InPlaceConvertToGreyscaleFloatType(&tmp, FIT_FLOAT);
    
	int width = FreeImage_GetWidth (tmp);
    int height = FreeImage_GetHeight (tmp);

    FIBITMAP *dst = FreeImage_Allocate (width, height, 8, 0, 0, 0);

    FIA_SetGreyLevelPalette (dst);

    FIAPOINT *sort_array = new FIAPOINT[width * height];
    FIAPOINT *hull_array = new FIAPOINT[width * height];

    float *ptr;
    register int i = 0;

    FIAPOINT pt;

    // Copy image pixels to 1d array
    for(register int y = 0; y < height; y++)
    {
        ptr = (float *) FreeImage_GetScanLine (tmp, y);

        for(register int x = 0; x < width; x++)
        {
            if (ptr[x])
            {
                pt.x = x;
                pt.y = y;
                sort_array[i++] = pt;
            }
        }
    }

    // sort the array by x and then y
    // Sort the peaks
 //   qsort (sort_array, i, sizeof (FIAPOINT), ComparePoints);
	// Should be already sorted in creating the point set

    int number_of_points = ChainHull_2D (sort_array, i, hull_array);

    delete sort_array;

//	FIA_DrawSolidGreyscalePolygon (dst, hull_array, number_of_points, 255, 0);
	
	// replace the above command with this more predictable loop
	for (int i=0; i<number_of_points-1; i++) {
		FIA_DrawOnePixelIndexLineFromTopLeft (dst, hull_array[i], hull_array[i+1], 255);
	}
	FIA_DrawOnePixelIndexLineFromTopLeft (dst, hull_array[number_of_points-1], hull_array[0], 255);
	dst = FIA_Fillholes(dst, 1);
	FreeImage_FlipVertical(dst);   // FIA_Draw is not from top,left

	delete hull_array;

    FreeImage_Unload (tmp);

    return dst;
}