// GflAXObj.cpp : Implementation of CGflAX
#include "StdAfx.h"
#pragma comment(lib, "wininet.lib")


/////////////////////////////////////////////////////////////////////////////
// CGflAX

#define GFLAX_VERSION "2.90"

#ifdef __TWAIN_SUPPORT__
	#include "scan.h"
#endif

STDMETHODIMP CGflAX::InterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] =
	{
		&IID_IGflAx
	};
	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

//--

#define WIDTHBYTES(i) ((((i)+31)/32) * 4)

GFL_ERROR GFLAPI ConvertDIBSectionIntoBitmap(const LPVOID pBits, GFL_BITMAP *bitmap)
{
	int bytes_per_line, ncolors;

		if (bitmap->BitsPerComponent > 8)
			return GFL_ERROR_BAD_PARAMETERS;

    switch (bitmap->Type)
    {
    case GFL_BINARY:
      ncolors = 2;
			break;
    case GFL_GREY:
    case GFL_COLORS:
      /*ncolors = 256; */
	    ncolors = bitmap->ColorUsed;
			break;
    default:
			ncolors = 0;
		}
		if (ncolors == 2)
			bytes_per_line = WIDTHBYTES(bitmap->Width);
		else
		if (ncolors == 0)
			bytes_per_line = (bitmap->Width * bitmap->ComponentsPerPixel + 3) & ~3;
		else
			bytes_per_line = (bitmap->Width + 3) & ~3;
	
		if (bitmap->Origin != GFL_BOTTOM_LEFT
			|| (bitmap->ComponentsPerPixel == 3 && bitmap->Type != GFL_BGR)
			|| (bitmap->ComponentsPerPixel == 4 && bitmap->Type != GFL_BGRA))
		{
		GFL_INT32 x, y;
		GFL_UINT8 * src, * dst;

			if (bitmap->ComponentsPerPixel >= 3)
			{
				for (y=0; y<bitmap->Height; y++)
				{
					if (bitmap->Origin & GFL_BOTTOM)
						dst = bitmap->Data + y * bitmap->BytesPerLine;
					else
						dst = bitmap->Data + (bitmap->Height - 1 - y) * bitmap->BytesPerLine;
				
					src = (GFL_UINT8 *)pBits + y * bytes_per_line;
					for (x=0; x<bitmap->Width; x++)
					{
						switch(bitmap->Type)
						{
						case GFL_RGBA:
							dst[2] = *src++;
							dst[1] = *src++;
							dst[0] = *src++;
							dst[3] = *src++;
							break;
						case GFL_BGR:
							dst[0] = *src++;
							dst[1] = *src++;
							dst[2] = *src++;
							break;
						case GFL_ABGR:
							dst[3] = *src++;
							dst[0] = *src++;
							dst[1] = *src++;
							dst[2] = *src++;
							break;
						case GFL_BGRA:
							dst[0] = *src++;
							dst[1] = *src++;
							dst[2] = *src++;
							dst[3] = *src++;
							break;
						default:
							dst[2] = *src++;
							dst[1] = *src++;
							dst[0] = *src++;
						}
						dst += bitmap->ComponentsPerPixel;
					}
				}
			}
			else
			{
				for (y=0; y<bitmap->Height; y++)
				{
					if (bitmap->Origin & GFL_BOTTOM)
						dst = bitmap->Data + y * bitmap->BytesPerLine;
					else
						dst = bitmap->Data + (bitmap->Height - 1 - y) * bitmap->BytesPerLine;
				
					memcpy(dst, (GFL_UINT8 *)pBits + y * bytes_per_line, bitmap->Width);
				}
			}
		}
		else
			memcpy(bitmap->Data, pBits, bytes_per_line * bitmap->Height);
	
		return GFL_NO_ERROR;
}

GFL_ERROR GFLAPI ConvertBitmapIntoDIBSection(const GFL_BITMAP *bitmap, HBITMAP *hDIB, LPVOID * lpBits)
{
	int bytes_per_line, ncolors;
	HANDLE handle;
	BITMAPINFOHEADER * pdib_info;
	HDC hDC;

		/*if (bitmap->BitsPerComponent > 8)
			return GFL_ERROR_BAD_PARAMETERS; */

    switch (bitmap->Type)
    {
    case GFL_BINARY:
      ncolors = 2;
			break;
    case GFL_GREY:
    case GFL_COLORS:
      /*ncolors = 256; */
	    ncolors = bitmap->ColorUsed;
			break;
    default:
			ncolors = 0;
		}
	
		if (ncolors == 2)
			bytes_per_line = WIDTHBYTES(bitmap->Width);
		else
		if (ncolors == 0)
			bytes_per_line = (bitmap->Width * bitmap->ComponentsPerPixel + 3) & ~3;
		else
			bytes_per_line = (bitmap->Width + 3) & ~3;
	
		pdib_info = (BITMAPINFOHEADER*)calloc(1, sizeof(BITMAPINFOHEADER) + ncolors * sizeof(RGBQUAD));
		if (pdib_info == NULL)
			return GFL_ERROR_NO_MEMORY;
	
		pdib_info->biSize         = sizeof(BITMAPINFOHEADER);
		pdib_info->biWidth        = bitmap->Width;
		pdib_info->biHeight       = bitmap->Height;
		pdib_info->biPlanes       = 1;
		pdib_info->biClrUsed      = 0;
		pdib_info->biBitCount     = (bitmap->BitsPerComponent > 8 ? 8 : bitmap->BitsPerComponent) * bitmap->ComponentsPerPixel;
		pdib_info->biCompression  = BI_RGB;
		pdib_info->biSizeImage    = 0;
		pdib_info->biClrImportant = 0;
		hDC = GetDC(NULL);

		if (bitmap->Type == GFL_GREY)
		{
			RGBQUAD* ptr = (RGBQUAD *)((GFL_UINT8 *)pdib_info + sizeof(BITMAPINFOHEADER));
			int i;
			for (i=0; i<256; i++)
				ptr[i].rgbRed = ptr[i].rgbGreen = ptr[i].rgbBlue = (BYTE)i;
		}
		else
		if (bitmap->Type == GFL_BINARY)
		{
			RGBQUAD* ptr = (RGBQUAD *)((GFL_UINT8 *)pdib_info + sizeof(BITMAPINFOHEADER));
			ptr[0].rgbRed = ptr[0].rgbGreen = ptr[0].rgbBlue = 0;
			ptr[1].rgbRed = ptr[1].rgbGreen = ptr[1].rgbBlue = 255;
		}
	
		handle = CreateDIBSection(hDC, (BITMAPINFO *)pdib_info, DIB_RGB_COLORS, lpBits, NULL, 0);
		ReleaseDC(NULL, hDC);
	
		if (handle)
		{
			if (bitmap->Origin != GFL_BOTTOM_LEFT
				|| (bitmap->ComponentsPerPixel == 3 && bitmap->Type != GFL_BGR)
				|| (bitmap->ComponentsPerPixel == 4 && bitmap->Type != GFL_BGRA)
				|| bitmap->BitsPerComponent > 8)
			{
				GFL_INT32 x, y;
				GFL_UINT8* dst;
			
				if (bitmap->BitsPerComponent > 8)
				{
					GFL_UINT16* src;
					for (y=0; y<bitmap->Height; y++)
					{
						if (bitmap->Origin & GFL_BOTTOM)
							src = (GFL_UINT16*)(bitmap->Data + y * bitmap->BytesPerLine);
						else
							src = (GFL_UINT16*)(bitmap->Data + (bitmap->Height - 1 - y) * bitmap->BytesPerLine);
					
						dst = (GFL_UINT8 *)*lpBits + y * bytes_per_line;
						for (x=0; x<bitmap->Width; x++)
						{
							switch(bitmap->Type)
							{
							case GFL_GREY:
								dst[0] = *src++ >> 8;
								break;

							case GFL_RGBA:
								dst[2] = *src++ >> 8;
								dst[1] = *src++ >> 8;
								dst[0] = *src++ >> 8;
								dst[3] = *src++ >> 8;
								break;
							case GFL_BGR:
								dst[0] = *src++ >> 8;
								dst[1] = *src++ >> 8;
								dst[2] = *src++ >> 8;
								break;
							case GFL_ABGR:
								dst[3] = *src++ >> 8;
								dst[0] = *src++ >> 8;
								dst[1] = *src++ >> 8;
								dst[2] = *src++ >> 8;
								break;
							case GFL_BGRA:
								dst[0] = *src++ >> 8;
								dst[1] = *src++ >> 8;
								dst[2] = *src++ >> 8;
								dst[3] = *src++ >> 8;
								break;
							default:
								dst[2] = *src++ >> 8;
								dst[1] = *src++ >> 8;
								dst[0] = *src++ >> 8;
							}
							dst += bitmap->ComponentsPerPixel;
						}
					}
				}
				else
				{
					GFL_UINT8* src;
					if (bitmap->ComponentsPerPixel >= 3)
					{
						for (y=0; y<bitmap->Height; y++)
						{
							if (bitmap->Origin & GFL_BOTTOM)
								src = bitmap->Data + y * bitmap->BytesPerLine;
							else
								src = bitmap->Data + (bitmap->Height - 1 - y) * bitmap->BytesPerLine;
						
							dst = (GFL_UINT8 *)*lpBits + y * bytes_per_line;
							for (x=0; x<bitmap->Width; x++)
							{
								switch(bitmap->Type)
								{
								case GFL_RGBA:
									dst[2] = *src++;
									dst[1] = *src++;
									dst[0] = *src++;
									dst[3] = *src++;
									break;
								case GFL_BGR:
									dst[0] = *src++;
									dst[1] = *src++;
									dst[2] = *src++;
									break;
								case GFL_ABGR:
									dst[3] = *src++;
									dst[0] = *src++;
									dst[1] = *src++;
									dst[2] = *src++;
									break;
								case GFL_BGRA:
									dst[0] = *src++;
									dst[1] = *src++;
									dst[2] = *src++;
									dst[3] = *src++;
									break;
								default:
									dst[2] = *src++;
									dst[1] = *src++;
									dst[0] = *src++;
								}
								dst += bitmap->ComponentsPerPixel;
							}
						}
					}
					else
					{
						for (y=0; y<bitmap->Height; y++)
						{
							if (bitmap->Origin & GFL_BOTTOM)
								src = bitmap->Data + y * bitmap->BytesPerLine;
							else
								src = bitmap->Data + (bitmap->Height - 1 - y) * bitmap->BytesPerLine;
						
							memcpy((GFL_UINT8 *)*lpBits + y * bytes_per_line, src, bitmap->Width);
						}
					}
				}
			}
			else
				memcpy(*lpBits, bitmap->Data, bytes_per_line * bitmap->Height);
		}
	
		*hDIB = (HBITMAP)handle;

		free(pdib_info);

		return GFL_NO_ERROR;
}

void GFLAPI gflAddText2(HBITMAP hBitmap, INT bitmap_width, BSTR text, BSTR font_name, GFL_INT32 x, GFL_INT32 y, GFL_INT32 font_size, GFL_INT32 orientation, GFL_BOOL italic, GFL_BOOL bold, GFL_BOOL strike_out, GFL_BOOL underline, GFL_BOOL antialias, const GFL_COLOR *color)
{
	HDC hDC, hMemoryDC;
	HBITMAP hOldBM;
	HFONT hFont, hOldFont;
	LOGFONTW log_font;
	RECT rect;

	hDC = GetDC(NULL);
	hMemoryDC = CreateCompatibleDC(hDC);
	ReleaseDC(NULL, hDC);
	hOldBM = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

	memset(&log_font, 0, sizeof(log_font));
	log_font.lfHeight = font_size;
	log_font.lfWidth = 0;
	log_font.lfEscapement = orientation * 10;
	log_font.lfOrientation = 0;
	log_font.lfWeight = bold ? FW_BOLD : FW_NORMAL;
	log_font.lfItalic = italic ? TRUE : FALSE;
	log_font.lfUnderline = underline ? TRUE : FALSE;
	log_font.lfStrikeOut = strike_out ? TRUE : FALSE;
	log_font.lfOutPrecision = OUT_DEFAULT_PRECIS;
	log_font.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	log_font.lfQuality = (antialias ? ANTIALIASED_QUALITY : PROOF_QUALITY);
	log_font.lfCharSet = DEFAULT_CHARSET;
	log_font.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
	wcsncpy(log_font.lfFaceName, font_name, LF_FACESIZE);
	
	hFont = CreateFontIndirectW(&log_font);
	hOldFont = (HFONT)SelectObject(hMemoryDC, hFont);

	rect.left = x;
	rect.top = y;
	rect.right = bitmap_width;
	rect.bottom = y+font_size;
	SetBkMode(hMemoryDC, TRANSPARENT);
	SetTextColor(hMemoryDC, RGB(color->Red, color->Green, color->Blue));
	TextOutW(hMemoryDC, x, y, text, (int)wcslen(text));
	//DrawText(hMemoryDC, text, strlen(text), &rect, DT_TOP | DT_NOCLIP);

	SelectObject(hMemoryDC, hOldFont);
	DeleteObject(hFont);

	SelectObject(hMemoryDC, hOldBM);
	DeleteDC(hMemoryDC);
}

//--

#ifdef _DEBUG
static int numInstance=0;
#endif

CGflAX::CGflAX()
{
	m_bitmap = NULL;
	m_pageIndex = 1;
	m_bitmapFilename[0] = '\0';

	m_GIFInterlaced = FALSE;
	m_JPEGProgressive = FALSE;
	m_JPEGQuality = 80;
	m_PNGCompression = 7;
	m_saveFormatIndex = 0;
	m_saveCompression = AX_NoCompression;
	m_keepMetadata = TRUE;

	m_errorLanguage = AX_English;

	wcsncpy(m_fontName, L"MS Sans Serif", LF_FACESIZE);
	m_fontSize = 8;
	m_fontBold = FALSE;
	m_fontItalic = FALSE;
	m_fontStrikeOut = FALSE;
	m_fontUnderline = FALSE;
	m_fontAntiAlias = TRUE;
	m_fontOrientation = 0;

	m_hBitmap = NULL;
	m_pBits = NULL;

	m_epsDpi = 72;
	m_useEXIFDPI = FALSE;
	m_useEmbeddedThumbnail = FALSE;
	m_updateEmbeddedThumbnail = FALSE;

	m_lineWidth = 1;
	m_fillColor = RGB(255, 255, 255);
	m_lineColor = RGB(0, 0, 0);

	m_useTransparency = FALSE;
	m_maskColor = RGB(255, 255, 255);
	m_backColor = RGB(255, 255, 255);
	m_currentZoom = 100;
	m_scaleToGrey = FALSE;

	m_epsWidth = m_epsHeight = -1;

#ifdef _DEBUG
	if (numInstance++ == 0)
	{
		AllocConsole();
		freopen("conin$", "r", stdin);
		freopen("conout$", "w", stdout);
		freopen("conout$", "w", stderr);
	}
#endif

	gflLibraryInit();
}

CGflAX::~CGflAX()
{
#ifdef __TWAIN_SUPPORT__
	TWAINExit();
#endif

	if (m_bitmap)
		gflFreeBitmap(m_bitmap);

	gflLibraryExit();

#ifdef _DEBUG
	if (--numInstance == 0)
	{
		//getchar();
		FreeConsole();
	}
#endif
}

void CGflAX::FreeCurrentBitmap()
{
	if (m_bitmap)
		gflFreeBitmap(m_bitmap);
	m_bitmap = NULL;
	m_bitmapFilename[0] = '\0';
}

BOOL CGflAX::IsImageLoaded()
{
	return m_bitmap != NULL;
}

const char * CGflAX::GetErrorString(GFL_ERROR error)
{
	const char * str="";

	if (m_errorLanguage == AX_English)
		switch(error)
		{
		case 1000:
			str = "No file opened";
			break;
		case 1001:
			str = "The specified mode is incorrect";
			break;
		case 1002:
			str = "The dithering is incorrect";
			break;
		case 1003:
			str = "The authorized interval is an integer between 0 and 10";
			break;
		case 1004:
			str = "The authorized interval is an integer between 1 and 7";
			break;
		case 1005:
			str = "The authorized interval is an integer between 2 and 15";
			break;
		case 1006:
			str = "No canvas to realise operation";
			break;

		default:
			str = gflGetErrorString(error);
		}
	else
		switch(error)
		{
		case GFL_ERROR_FILE_OPEN:
			str = "Impossible d'ouvrir le fichier";
			break;
		case GFL_ERROR_FILE_READ:
			str = "Impossible de lire le fichier";
			break;
		case GFL_ERROR_FILE_CREATE:
			str = "Impossible de créer le fichier";
			break;
		case GFL_ERROR_FILE_WRITE:
			str = "Impossible d'écrire le fichier";
			break;
		case GFL_ERROR_NO_MEMORY:
			str = "Pas de mémoire disponible";
			break;
		case GFL_ERROR_UNKNOWN_FORMAT:
			str = "Format d'image inconnu";
			break;
		case GFL_ERROR_BAD_BITMAP:
			str = "Le format ne permet pas de sauver ce type d'image";
			break;
		case GFL_ERROR_BAD_FORMAT_INDEX:
			str = "Index de Format incorrect";
			break;
		case GFL_ERROR_BAD_PARAMETERS:
			str = "Mauvais paramètres";
			break;
		case GFL_UNKNOWN_ERROR:
			str = "Erreur inconnu";
			break;
		case 1000:
			str = "Aucun fichier ouvert";
			break;
		case 1001:
			str = "Le mode spécifié est incorrect";
			break;
		case 1002:
			str = "Le tramage spécifié est incorrect";
			break;
		case 1003:
			str = "L'intervalle autorisé est un entier entre 0 et 100";
			break;
		case 1004:
			str = "L'intervalle autorisé est un entier entre 1 et 7";
			break;
		case 1005:
			str = "L'intervalle autorisé est un entier entre 2 et 15";
			break;
		case 1006:
			str = "Aucun espace de travail pour réaliser l'opération";
			break;

		default:
			str = gflGetErrorString(error);
		}

	return str;
}

GFL_COLOR CGflAX::OLEToColor(OLE_COLOR ole_color)
{
	GFL_COLOR
		color;
	color.Red   = (GFL_UINT16)((ole_color      ) & 0xff);
	color.Green = (GFL_UINT16)((ole_color >>  8) & 0xff);
	color.Blue  = (GFL_UINT16)((ole_color >> 16) & 0xff);
	return color;
}

OLE_COLOR CGflAX::ColorToOLE(GFL_COLOR & color)
{
	return RGB(color.Red, color.Green, color.Blue);
}

//
// Method
//
STDMETHODIMP CGflAX::LoadBitmap(BSTR filename)
{
	GFL_UINT8 * buffer = NULL;
	DWORD length = 0;

	if (! _wcsnicmp(filename, L"http://", 7))
		buffer = UploadFilename(filename, &length);

	GFL_LOAD_PARAMS
		params;
	gflGetDefaultLoadParams(&params);
	params.Flags = /*GFL_LOAD_SKIP_ALPHA|*/GFL_LOAD_METADATA;
	params.Origin = GFL_BOTTOM_LEFT;
	params.ColorModel = GFL_BGRA; //GFL_BGR;
	params.LinePadding = 4;
	params.ImageWanted = m_pageIndex - 1;

	params.EpsWidth = m_epsWidth;
	params.EpsHeight = m_epsHeight;
	params.EpsDpi = (GFL_UINT16)m_epsDpi;

	// Free old bitmap
	FreeCurrentBitmap();

	GFL_ERROR
		error;
	if (buffer)
		error = gflLoadBitmapFromMemory(buffer, length, &m_bitmap, &params, &m_fileInformations);
	else
		error = gflLoadBitmapW(filename, &m_bitmap, &params, &m_fileInformations);
	if (error != GFL_NO_ERROR)
	{
		if (buffer)
			free(buffer);

		Error(GetErrorString(error));
		return E_FAIL;
	}

	if (buffer == NULL
		&& (m_fileInformations.FormatIndex == gflGetFormatIndexByName("ps")
			|| m_fileInformations.FormatIndex == gflGetFormatIndexByName("pdf")))
		gflGetFileInformationW(filename, m_fileInformations.FormatIndex, &m_fileInformations);

	int xdpi, ydpi;
	if (m_useEXIFDPI
		&& gflGetEXIFDPI(m_bitmap, &xdpi, &ydpi))
	{
		m_bitmap->Xdpi = (GFL_UINT16)xdpi;
		m_bitmap->Ydpi = (GFL_UINT16)ydpi;
	}

	// Keep index of format to use
	m_saveFormatIndex = m_fileInformations.FormatIndex;
	if (! gflFormatIsWritableByIndex(m_saveFormatIndex))
		m_saveFormatIndex = gflGetFormatIndexByName("jpeg");
	wcsncpy(m_bitmapFilename, filename, MAX_PATH);

	if (buffer)
		free(buffer);

	return S_OK;
}

STDMETHODIMP CGflAX::LoadThumbnail(BSTR filename, long width, long height)
{
	GFL_LOAD_PARAMS
		params;
	gflGetDefaultLoadParams(&params);
	params.Flags = GFL_LOAD_SKIP_ALPHA | GFL_LOAD_METADATA;
	params.Origin = GFL_BOTTOM_LEFT;
	params.ColorModel = GFL_BGR;
	params.LinePadding = 4;
	params.ImageWanted = m_pageIndex - 1;

	params.EpsWidth = m_epsWidth;
	params.EpsHeight = m_epsHeight;
	params.EpsDpi = (GFL_UINT16)m_epsDpi;

	if (m_useEmbeddedThumbnail)
		params.Flags |= GFL_LOAD_ORIGINAL_EMBEDDED_THUMBNAIL;

	// Free old bitmap
	FreeCurrentBitmap();

	GFL_ERROR
		error;
	error = gflLoadThumbnailW(filename, width, height, &m_bitmap, &params, &m_fileInformations);
	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	// Keep index of format to use
	m_saveFormatIndex = m_fileInformations.FormatIndex;
	if (! gflFormatIsWritableByIndex(m_saveFormatIndex))
		m_saveFormatIndex = gflGetFormatIndexByName("jpeg");
	wcsncpy(m_bitmapFilename, filename, MAX_PATH);

	return S_OK;
}

STDMETHODIMP CGflAX::NewBitmap(long width, long height, OLE_COLOR back_color)
{
	FreeCurrentBitmap();

	GFL_COLOR
		color = OLEToColor(back_color);

	m_bitmap = gflAllockBitmap(GFL_BGR, width, height, 4, &color);
	if (m_bitmap == NULL)
	{
		Error(GetErrorString(GFL_ERROR_NO_MEMORY));
		return E_FAIL;
	}
	m_bitmap->Origin = GFL_BOTTOM_LEFT;
	return S_OK;
}

STDMETHODIMP CGflAX::ReloadBitmap()
{
	if (m_bitmap
		&& m_bitmapFilename[0] != '\0')
		return LoadBitmap(m_bitmapFilename);

	return S_OK;
}

STDMETHODIMP CGflAX::NextPage(long *pVal)
{
	if (m_pageIndex < m_fileInformations.NumberOfImages)
	{
		*pVal = ++m_pageIndex;
		ReloadBitmap();
	}

	return S_OK;
}

STDMETHODIMP CGflAX::PreviousPage(long *pVal)
{
	if (m_pageIndex > 1)
	{
		*pVal = --m_pageIndex;
		ReloadBitmap();
	}

	return S_OK;
}

STDMETHODIMP CGflAX::SaveBitmap(BSTR filename)
{
	if (m_bitmap == NULL)
	{
		Error(GetErrorString(1000));
		return E_FAIL;
	}

	GFL_SAVE_PARAMS
		params;

	gflGetDefaultSaveParams(&params);
	params.Flags = GFL_SAVE_REPLACE_EXTENSION | GFL_SAVE_ANYWAY;
	params.FormatIndex = m_saveFormatIndex;
	params.Compression = (GFL_UINT16)GetCompression(m_saveFormatIndex);
	params.Interlaced = (GFL_BOOL)m_GIFInterlaced;
	params.CompressionLevel = (GFL_INT16)m_PNGCompression;
	params.Progressive = (GFL_BOOL)m_JPEGProgressive;
	params.Quality = (GFL_UINT16)m_JPEGQuality;

	LPVOID saved_metadata = NULL;
	if (! m_keepMetadata)
	{
		saved_metadata = m_bitmap->MetaData;
		m_bitmap->MetaData = NULL;
	}
	//	gflBitmapRemoveMetaData(m_bitmap);

	if (m_updateEmbeddedThumbnail
		&& m_saveFormatIndex == gflGetFormatIndexByName("jpeg"))
		gflBitmapSetEXIFThumbnail(m_bitmap, NULL);

	GFL_ERROR
		error;
	error = gflSaveBitmapW(filename ? filename : m_bitmapFilename, m_bitmap, &params);

	if (! m_keepMetadata)
		m_bitmap->MetaData = saved_metadata;

	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	return S_OK;
}

STDMETHODIMP CGflAX::SetMetadataRefFile(BSTR filename)
{
	GFL_LOAD_PARAMS
		params;
	gflGetDefaultLoadParams(&params);
	params.Flags = GFL_LOAD_METADATA;
	params.ColorModel = GFL_RGB;
	params.LinePadding = 4;
	params.ImageWanted = m_pageIndex - 1;

	params.EpsWidth = m_epsWidth;
	params.EpsHeight = m_epsHeight;
	params.EpsDpi = (GFL_UINT16)m_epsDpi;

	GFL_ERROR
		error;
	GFL_BITMAP
		* tmp_bitmap;
	error = gflLoadBitmapW(filename, &tmp_bitmap, &params, NULL);
	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	if (m_bitmap)
	{
		gflBitmapRemoveMetaData(m_bitmap);
		m_bitmap->MetaData = tmp_bitmap->MetaData;
		tmp_bitmap->MetaData = NULL;
	}

	gflFreeBitmap(tmp_bitmap);
	return S_OK;
}

//
// Property
//

STDMETHODIMP CGflAX::About(VARIANT * ret)
{
	OLECHAR string[256];
    _snwprintf(string, 256, L"GflAX DLL %hs- GFL %hs %ls",
        GFLAX_VERSION,
        GFL_VERSION,
        gflGetNumberOfFormat() < 30 ? L" (Light)" : L" (Standard)");
	*ret = _variant_t(string);
	return S_OK;
}

STDMETHODIMP CGflAX::get_Language(AX_Language *pVal)
{
	*pVal = m_errorLanguage;

	return S_OK;
}

STDMETHODIMP CGflAX::put_Language(AX_Language newVal)
{
	m_errorLanguage = newVal;

	return S_OK; //MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 5/*51+1001*/);
}

STDMETHODIMP CGflAX::get_SaveCompression(AX_SaveCompressions *pVal)
{
	*pVal = m_saveCompression;

	return S_OK;
}

STDMETHODIMP CGflAX::put_SaveCompression(AX_SaveCompressions newVal)
{
	m_saveCompression = newVal;

	return S_OK;
}

typedef struct {
		const char * name;
		AX_SaveFormats format;
	} GFL_AX_TABLE;

static GFL_AX_TABLE GflAXTable[] = {
		{ "jpeg", AX_JPEG },
		{ "gif", AX_GIF },
		{ "png", AX_PNG },
		{ "bmp", AX_BMP },
		{ "tiff", AX_TIFF },
		{ "tga", AX_TGA },
		{ "pcx", AX_PCX },
		{ "xpm", AX_XPM },
		{ "iff", AX_IFF },
		{ "soft", AX_SOFT },
		{ "sgi", AX_SGI },
		{ "dcx", AX_DCX },
		{ "pbm", AX_PBM },
		{ "pgm", AX_PGM },
		{ "ppm", AX_PPM },
		{ "pnm", AX_PNM },
		{ "miff", AX_MIFF },
		{ "xbm", AX_XBM },
		{ "ico", AX_ICO },
//		{ "psion3", AX_PSION3 },
//		{ "psion5", AX_PSION5 },
//		{ "palm", AX_PALM },
//		{ "emf", AX_EMF },
//		{ "vista", AX_VISTA },
//		{ "alias", AX_ALIAS },
//		{ "rla", AX_RLA },
//		{ "cin", AX_CIN },
//		{ "dkb", AX_DKB },
//		{ "qrt", AX_QRT },
//		{ "vivid", AX_VIVID },
//		{ "mtv", AX_MTV },
//		{ "ray", AX_RAY },
//		{ "jif", AX_JIF },
//		{ "gpat", AX_GPAT },
//		{ "grob", AX_GROB },
//		{ "biorad", AX_BIORAD },
//		{ "rad", AX_RAD },
//		{ "prc", AX_PRC },
//		{ "wrl", AX_WRL },
//		{ "pcl", AX_PCL },
//		{ "wbmp", AX_WBMP },
		{ "uyvyi", AX_UYVYI },
		{ "uyvy", AX_UYVY },
//		{ "raw", AX_RAW },
		{ "jp2", AX_JPEG2000 },
		{ "jbig", AX_JBIG },
		{ "fpx", AX_FPX },
		{ "ps", AX_PS },
	};

STDMETHODIMP CGflAX::get_SaveFormat(AX_SaveFormats *pVal)
{
	INT
		i;
	const char *
		current_format = gflGetFormatNameByIndex(m_saveFormatIndex);
	for (i=0; i<sizeof(GflAXTable)/sizeof(GFL_AX_TABLE); i++)
	{
		if (! _stricmp(GflAXTable[i].name, current_format))
		{
			*pVal = GflAXTable[i].format;
			return S_OK;
		}
	}

	Error(GetErrorString(GFL_ERROR_UNKNOWN_FORMAT));
	return E_FAIL;
}

STDMETHODIMP CGflAX::put_SaveFormat(AX_SaveFormats newVal)
{
	INT
		i;
	for (i=0; i<sizeof(GflAXTable)/sizeof(GFL_AX_TABLE); i++)
	{
		if (newVal == GflAXTable[i].format)
		{
			m_saveFormatIndex = gflGetFormatIndexByName(GflAXTable[i].name);
			return S_OK;
		}
	}

	Error(GetErrorString(GFL_ERROR_UNKNOWN_FORMAT));
	return E_FAIL;
}

STDMETHODIMP CGflAX::put_SaveFormatName(BSTR newVal)
{
    USES_CONVERSION;

	m_saveFormatIndex = gflGetFormatIndexByName(W2A(newVal));
	if (m_saveFormatIndex == -1)
	{
		Error(GetErrorString(GFL_ERROR_UNKNOWN_FORMAT));
		return E_FAIL;
	}
	return S_OK;
}

STDMETHODIMP CGflAX::put_SetPluginsPathname(BSTR newVal)
{
	gflLibraryExit();
	gflSetPluginsPathnameW(newVal);
	gflLibraryInit();

	return S_OK;
}

STDMETHODIMP CGflAX::put_SaveKeepMetadata(BOOL newVal)
{
	m_keepMetadata = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_SaveJPEGQuality(long *pVal)
{
	*pVal = m_JPEGQuality;

	return S_OK;
}

STDMETHODIMP CGflAX::put_SaveJPEGQuality(long newVal)
{
	if (newVal > 100
		|| newVal <= 0)
	{
		Error(GetErrorString(1003));
		return E_FAIL;
	}

	m_JPEGQuality = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_SaveGIFInterlaced(BOOL *pVal)
{
	*pVal = m_GIFInterlaced;

	return S_OK;
}

STDMETHODIMP CGflAX::put_SaveGIFInterlaced(BOOL newVal)
{
	m_GIFInterlaced = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_SaveJPEGProgressive(BOOL *pVal)
{
	*pVal = m_JPEGProgressive;

	return S_OK;
}

STDMETHODIMP CGflAX::put_SaveJPEGProgressive(BOOL newVal)
{
	m_JPEGProgressive = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_SavePNGCompression(long *pVal)
{
	*pVal = m_PNGCompression;

	return S_OK;
}

STDMETHODIMP CGflAX::put_SavePNGCompression(long newVal)
{
	if (newVal >= 9
		|| newVal <= 0)
	{
		Error(GetErrorString(1004));
		return E_FAIL;
	}

	m_PNGCompression = (GFL_INT16)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_SaveFormatDescription(VARIANT *pVal)
{
	GFL_FORMAT_INFORMATION
		info;

	gflGetFormatInformationByIndex(m_saveFormatIndex, &info);

	*pVal = _variant_t(info.Description);

	return S_OK;
}

STDMETHODIMP CGflAX::get_EpsWidth(long *pVal)
{
	*pVal = m_epsWidth;

	return S_OK;
}

STDMETHODIMP CGflAX::put_EpsWidth(long newVal)
{
	m_epsWidth = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_EpsHeight(long *pVal)
{
	*pVal = m_epsHeight;

	return S_OK;
}

STDMETHODIMP CGflAX::put_EpsHeight(long newVal)
{
	m_epsHeight = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_EpsDpi(long *pVal)
{
	*pVal = m_epsDpi;

	return S_OK;
}

STDMETHODIMP CGflAX::put_EpsDpi(long newVal)
{
	m_epsDpi = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_Width(long *pVal)
{
	if (IsImageLoaded())
		*pVal = m_bitmap->Width;

	return S_OK;
}

STDMETHODIMP CGflAX::get_Height(long *pVal)
{
	if (IsImageLoaded())
		*pVal = m_bitmap->Height;

	return S_OK;
}

STDMETHODIMP CGflAX::get_Xdpi(short *pVal)
{
	if (IsImageLoaded())
		*pVal = m_bitmap->Xdpi;

	return S_OK;
}

STDMETHODIMP CGflAX::get_Ydpi(short *pVal)
{
	if (IsImageLoaded())
		*pVal = m_bitmap->Ydpi;

	return S_OK;
}

STDMETHODIMP CGflAX::get_EnableLZW(BOOL *pVal)
{
	*pVal = m_enableLZW;

	return S_OK;
}

STDMETHODIMP CGflAX::put_EnableLZW(BOOL newVal)
{
	gflEnableLZW(m_enableLZW = (GFL_BOOL)newVal);

	return S_OK;
}

STDMETHODIMP CGflAX::get_NumberOfImages(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.NumberOfImages;

	return S_OK;
}

STDMETHODIMP CGflAX::get_BitmapType(AX_Type *pVal)
{
	if (IsImageLoaded())
		switch(m_bitmap->Type)
		{
		case GFL_BINARY:
			*pVal = AX_Binary;
			break;
		case GFL_GREY:
			*pVal = AX_Grey;
			break;
		case GFL_COLORS:
			*pVal = AX_Colors;
			break;
		default:
			*pVal = AX_TrueColors;
			break;
		}

	return S_OK;
}

STDMETHODIMP CGflAX::get_ColorModel(BSTR/*VARIANT*/ *pVal)
{
    USES_CONVERSION;

	//*pVal = _variant_t(gflGetLabelForColorModel(fileInformations.ColorModel));
	//CComBSTR bstrResult = A2OLE(gflGetLabelForColorModel(m_fileInformations.ColorModel));
	//*pVal = bstrResult;
	*pVal = SysAllocString(A2OLE(gflGetLabelForColorModel(m_fileInformations.ColorModel)));
	return S_OK;
}

STDMETHODIMP CGflAX::get_OriginalXdpi(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.Xdpi;

	return S_OK;
}

STDMETHODIMP CGflAX::get_OriginalYdpi(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.Ydpi;

	return S_OK;
}

STDMETHODIMP CGflAX::get_OriginalWidth(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.Width;

	return S_OK;
}

STDMETHODIMP CGflAX::get_OriginalHeight(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.Height;

	return S_OK;
}

STDMETHODIMP CGflAX::get_OriginalSize(long *pVal)
{
	if (m_bitmap)
		*pVal = m_fileInformations.FileSize;

	return S_OK;
}

STDMETHODIMP CGflAX::get_Page(long *pVal)
{
	*pVal = m_pageIndex;

	return S_OK;
}

STDMETHODIMP CGflAX::put_Page(long newVal)
{
	m_pageIndex = newVal;
	ReloadBitmap();

	return S_OK;
}

STDMETHODIMP CGflAX::get_NumberOfPages(long *pVal)
{
	if (IsImageLoaded())
		*pVal = m_fileInformations.NumberOfImages;
	else
		*pVal = 0;

	return S_OK;
}

STDMETHODIMP CGflAX::get_NumberOfColorUsed(long *pVal)
{
	*pVal = 0;
	if (IsImageLoaded())
		*pVal = gflGetNumberOfColorsUsed(m_bitmap);

	return S_OK;
}

//
// Property GFLE
//

STDMETHODIMP CGflAX::get_FontItalic(BOOL *pVal)
{
	*pVal = m_fontItalic;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontItalic(BOOL newVal)
{
	m_fontItalic = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontBold(BOOL *pVal)
{
	*pVal = m_fontBold;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontBold(BOOL newVal)
{
	m_fontBold = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontStrikeOut(BOOL *pVal)
{
	*pVal = m_fontStrikeOut;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontStrikeOut(BOOL newVal)
{
	m_fontStrikeOut = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontAntialias(BOOL *pVal)
{
	*pVal = m_fontAntiAlias;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontAntialias(BOOL newVal)
{
	m_fontAntiAlias = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontUnderline(BOOL *pVal)
{
	*pVal = m_fontUnderline;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontUnderline(BOOL newVal)
{
	m_fontUnderline = (GFL_BOOL)newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontName(BSTR *pVal)
{
	CComBSTR bstrResult(m_fontName);
	bstrResult.CopyTo(pVal);

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontName(BSTR newVal)
{
	wcsncpy(m_fontName, newVal, LF_FACESIZE);
	return S_OK;
}

STDMETHODIMP CGflAX::get_FontSize(long *pVal)
{
	*pVal = m_fontSize;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontSize(long newVal)
{
	m_fontSize = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FontOrientation(long *pVal)
{
	*pVal = m_fontOrientation;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FontOrientation(long newVal)
{
	m_fontOrientation = newVal;

	return S_OK;
}

//
// Process
//
#define PROCESS2(_t, _m) \
{\
	if (! IsImageLoaded())\
	{\
		Error(GetErrorString(_m)); \
		return E_FAIL; \
	}\
\
	GFL_ERROR\
		error; \
	if ((error = _t) != 0)\
	{\
		Error(GetErrorString(error)); \
		return E_FAIL; \
	}\
\
	return S_OK;\
}

#define PROCESS(_t) \
	PROCESS2(_t, 1000)

STDMETHODIMP CGflAX::FlipVertical()
{
	PROCESS(gflFlipVertical(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::FlipHorizontal()
{
	PROCESS(gflFlipHorizontal(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Resize(long width, long height)
{
	PROCESS(gflResize(m_bitmap, NULL, width, height, GFL_RESIZE_BILINEAR, 0));
}

STDMETHODIMP CGflAX::Crop(long x, long y, long width, long height)
{
	GFL_RECT
		rect;

	rect.x = x;
	rect.y = y;
	rect.w = width;
	rect.h = height;

	PROCESS(gflCrop(m_bitmap, NULL, &rect));
}

STDMETHODIMP CGflAX::AutoCrop(OLE_COLOR back_color, long tolerance)
{
	GFL_COLOR
		color = OLEToColor(back_color);

	PROCESS(gflAutoCrop(m_bitmap, NULL, &color, tolerance));
}

STDMETHODIMP CGflAX::ResizeCanvas(long width, long height, AX_CanvasMode canvas_mode, OLE_COLOR back_color)
{
	GFL_COLOR
		color = OLEToColor(back_color);

	PROCESS(gflResizeCanvas(m_bitmap, NULL, width, height, canvas_mode, &color));
}

STDMETHODIMP CGflAX::GetColorAt(long x, long y, OLE_COLOR *color)
{
	if (! IsImageLoaded())
	{
		Error(GetErrorString(1000));
		return E_FAIL;
	}

	GFL_ERROR
		error;
	GFL_COLOR
		gfl_color;
	if ((error = gflGetColorAt(m_bitmap, x, y, &gfl_color)) != 0)
		*color = 0;
	/*
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}
	*/
	*color = ColorToOLE(gfl_color);
	return S_OK;
}

STDMETHODIMP CGflAX::Rotate(double angle, OLE_COLOR back_color)
{
	GFL_COLOR
		color = OLEToColor(back_color);

	PROCESS(gflRotateFine(m_bitmap, NULL, angle, &color));
}

STDMETHODIMP CGflAX::ChangeColorDepth(AX_Mode mode, AX_Dither dither, AX_Palette palette)
{
	GFL_UINT16
		gfl_mode, gfl_dither;

	switch(mode)
	{
	case Ax_ToTrueColors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_BGR : GFL_MODE_TO_256GREY); break;
	case AX_ToBinary:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_8COLORS : GFL_MODE_TO_BINARY); break;
	case AX_To4Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_8COLORS : GFL_MODE_TO_4GREY); break;
	case AX_To8Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_8COLORS : GFL_MODE_TO_8GREY); break;
	case AX_To16Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_16COLORS : GFL_MODE_TO_16GREY); break;
	case AX_To32Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_32COLORS : GFL_MODE_TO_32GREY); break;
	case AX_To64Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_64COLORS : GFL_MODE_TO_64GREY); break;
	case AX_To128Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_128COLORS : GFL_MODE_TO_128GREY); break;
	case AX_To216Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_216COLORS : GFL_MODE_TO_216GREY); break;
	case AX_To256Colors:
		gfl_mode = (palette == Ax_ToColors ? GFL_MODE_TO_256COLORS : GFL_MODE_TO_256GREY); break;
	default:
		Error(GetErrorString(1001));
		return E_FAIL;
	}

	switch (dither)
	{
	case Ax_NoDither:
		gfl_dither = GFL_MODE_NO_DITHER; break;
	case AX_PatternDither:
		gfl_dither = GFL_MODE_PATTERN_DITHER; break;
	case AX_Haltone45Dither:
		gfl_dither = (mode == GFL_MODE_TO_BINARY ? GFL_MODE_HALTONE45_DITHER : GFL_MODE_NO_DITHER); break;
	case AX_Haltone90Dither:
		gfl_dither = (mode == GFL_MODE_TO_BINARY ? GFL_MODE_HALTONE90_DITHER : GFL_MODE_NO_DITHER); break;
	case AX_Adaptive:
		gfl_dither = GFL_MODE_NO_DITHER; break;
	case AX_FloydSteinberg:
		gfl_dither = (mode == GFL_MODE_TO_BINARY ? GFL_MODE_FLOYD_STEINBERG : GFL_MODE_NO_DITHER); break;
	default:
		Error(GetErrorString(1001));
		return E_FAIL;
	}

	PROCESS(gflChangeColorDepth(m_bitmap, NULL, gfl_mode, gfl_dither));
}

STDMETHODIMP CGflAX::ReplaceColor(OLE_COLOR color, OLE_COLOR new_color, long tolerance)
{
	GFL_COLOR
		gfl_color, gfl_new_color;
	gfl_color = OLEToColor(color);
	gfl_new_color = OLEToColor(new_color);

	PROCESS(gflReplaceColor(m_bitmap, NULL, &gfl_color, &gfl_new_color, tolerance));
}

//
// GFLE
//

STDMETHODIMP CGflAX::SwapColors(AX_SwapModel model)
{
	PROCESS(gflSwapColors(m_bitmap, NULL, (GFL_SWAPCOLORS_MODE)model));
}

STDMETHODIMP CGflAX::Gamma(double gamma)
{
	PROCESS(gflGamma(m_bitmap, NULL, gamma));
}

STDMETHODIMP CGflAX::Balance(OLE_COLOR color)
{
	GFL_COLOR
		gfl_color = OLEToColor(color);
	PROCESS(gflBalance(m_bitmap, NULL, &gfl_color));
}

STDMETHODIMP CGflAX::Adjust(long brightness, long contrast, double gamma)
{
	PROCESS(gflAdjust(m_bitmap, NULL, brightness, contrast, gamma));
}

STDMETHODIMP CGflAX::AdjustHLS(long hue, long lightness, long saturation)
{
	PROCESS(gflAdjustHLS(m_bitmap, NULL, hue, lightness, saturation));
}

STDMETHODIMP CGflAX::Hue(long value)
{
	return AdjustHLS(value, 0, 0);
}

STDMETHODIMP CGflAX::Lightness(long value)
{
	return AdjustHLS(0, value, 0);
}

STDMETHODIMP CGflAX::Saturation(long value)
{
	return AdjustHLS(0, 0, value);
}

STDMETHODIMP CGflAX::Sepia(long percent, OLE_COLOR color)
{
	if (color)
	{
		GFL_COLOR
			gfl_color = OLEToColor(color);
		PROCESS(gflSepiaEx(m_bitmap, NULL, percent, &gfl_color));
	}
	else
		PROCESS(gflSepia(m_bitmap, NULL, percent));

	return S_OK;
}

STDMETHODIMP CGflAX::TextOut(BSTR string, long x, long y, OLE_COLOR color)
{
	GFL_COLOR
		gfl_color = OLEToColor(color);

	if (m_hBitmap)
		gflAddText2(m_hBitmap, m_bitmap->Width, string, m_fontName, x, y, m_fontSize, m_fontOrientation, m_fontItalic, m_fontBold, m_fontStrikeOut, m_fontUnderline, m_fontAntiAlias, &gfl_color);
	else
		gflAddTextW(m_bitmap, string, m_fontName, x, y, m_fontSize, m_fontOrientation, m_fontItalic, m_fontBold, m_fontStrikeOut, m_fontUnderline, m_fontAntiAlias, &gfl_color);
	return S_OK;
}

typedef class {
	public:
		GFL_UINT32 currentSize;
		GFL_UINT32 currentAllocatedSize;
		GFL_UINT32 currentPosition;
		GFL_UINT8 * ptr;
	} MY_DATA;

static GFL_UINT32 GFLAPI myWriteFunction2(GFL_HANDLE handle, const void *buffer, GFL_UINT32 size)
{
	MY_DATA *ptr = (MY_DATA *)handle;

	if (ptr->currentPosition+size > ptr->currentAllocatedSize)
	{
		/*if (ptr->noAlloc)
			return 0; */

		while (ptr->currentAllocatedSize < ptr->currentSize+size)
			ptr->currentAllocatedSize += 32000;
	
		if (ptr->ptr == NULL)
			ptr->ptr = (GFL_UINT8*)malloc(ptr->currentAllocatedSize);
		else
			ptr->ptr = (GFL_UINT8*)realloc(ptr->ptr, ptr->currentAllocatedSize);

		if (ptr->ptr == NULL)
			return 0;
	}
	memcpy(ptr->ptr+ptr->currentPosition, buffer, size);
	ptr->currentPosition += size;
	ptr->currentSize = max(ptr->currentPosition, ptr->currentSize);
	return size;
}

static GFL_UINT32 GFLAPI myTellFunction2(GFL_HANDLE handle)
{
	MY_DATA *ptr = (MY_DATA *)handle;
	return ptr->currentPosition;
}

static GFL_UINT32 GFLAPI mySeekFunction2(GFL_HANDLE handle, GFL_INT32 offset, GFL_INT32 origin)
{
	MY_DATA *ptr = (MY_DATA *)handle;
	if (origin == SEEK_CUR)
		offset += ptr->currentPosition;
	else
	if (origin == SEEK_END)
		offset = ptr->currentSize-ptr->currentPosition;

	if (offset >= 0 && (GFL_UINT32)offset > ptr->currentSize)
		return (GFL_UINT32)-1;

	ptr->currentPosition = offset;
	return ptr->currentPosition;
}

static GFL_UINT32 GFLAPI MyWriteFunction(void * ptr, const void *buffer, GFL_UINT32 size)
{
	MY_DATA * data = (MY_DATA *)ptr;

	if (data->currentPosition+size >= data->currentAllocatedSize)
	{
		data->currentAllocatedSize += 16384;
		if (data->ptr == NULL)
			data->ptr = (GFL_UINT8 *)gflMemoryAlloc(data->currentAllocatedSize);
		else
			data->ptr = (GFL_UINT8 *)gflMemoryRealloc(data->ptr, data->currentAllocatedSize);

		if (data->ptr == NULL)
			return 0;
	}
	memcpy(data->ptr + data->currentPosition, buffer, size);
	data->currentPosition += size;
	data->currentSize += size;
	return size;
}

static GFL_UINT32 GFLAPI MyTellFunction(void * ptr)
{
	MY_DATA * data = (MY_DATA *)ptr;

	return data->currentPosition;
}

static GFL_UINT32 GFLAPI MySeekFunction(void * ptr, GFL_INT32 offset, GFL_INT32 origin)
{
    UNREFERENCED_PARAMETER(origin);

	MY_DATA * data = (MY_DATA *)ptr;

	if (offset >= 0 && (GFL_UINT32)offset >= data->currentSize)
		return (GFL_UINT32)-1;
	data->currentPosition = offset;
	return 0;
}

STDMETHODIMP CGflAX::SendBinary(SAFEARRAY ** pVal)
{
	GFL_SAVE_PARAMS
		params;
	gflGetDefaultSaveParams(&params);
	params.Callbacks.Write = myWriteFunction2;
	params.Callbacks.Tell = myTellFunction2;
	params.Callbacks.Seek = mySeekFunction2;
	params.Flags = GFL_SAVE_ANYWAY;
	params.FormatIndex = m_saveFormatIndex;
	params.Compression = (GFL_UINT16)m_saveCompression;
	params.Interlaced = m_GIFInterlaced;
	params.CompressionLevel = m_PNGCompression;
	params.Progressive = m_JPEGProgressive;
	params.Quality = (GFL_UINT16)m_JPEGQuality;

	MY_DATA
		my_data;
	my_data.currentSize = 0;
	my_data.currentAllocatedSize = 0;
	my_data.currentPosition = 0;
	my_data.ptr = NULL;

	GFL_ERROR
		error = gflSaveBitmapIntoHandle(&my_data, m_bitmap, &params);

	if (error != GFL_NO_ERROR)
	{
		if (my_data.ptr)
			free(my_data.ptr);

		Error(GetErrorString(error));
		return E_FAIL;
	}

	SAFEARRAY *
		psa = SafeArrayCreateVector(VT_I1, 0, my_data.currentSize);
	void *
		pData;
	SafeArrayAccessData(psa, &pData);
	memcpy(pData, my_data.ptr, my_data.currentSize);
	SafeArrayUnaccessData(psa);
	*pVal = psa;

	free(my_data.ptr);

	return S_OK;
}

STDMETHODIMP CGflAX::SetBlob(SAFEARRAY ** pVal)
{
	return SendBinary(pVal);
}

typedef class {
	public:
		GFL_UINT32 length;
		GFL_UINT32 currentPosition;
		GFL_UINT8 * ptr;
	} MY_DATA2;

static GFL_UINT32 GFLAPI MyReadFunction2(void * ptr, void *buffer, GFL_UINT32 size)
{
	MY_DATA2 * data = (MY_DATA2 *)ptr;

	if (data->currentPosition+size >= data->length)
		size = data->length - data->currentPosition;

	memcpy(buffer, data->ptr + data->currentPosition, size);
	data->currentPosition += size;
	return size;
}
   
static GFL_UINT32 GFLAPI MyTellFunction2(void * ptr)
{
	MY_DATA2 * data = (MY_DATA2 *)ptr;

	return data->currentPosition;
}

static GFL_UINT32 GFLAPI MySeekFunction2(void * ptr, GFL_INT32 offset, GFL_INT32 origin)
{
	MY_DATA2 * data = (MY_DATA2 *)ptr;

	switch(origin)
	{
	case 1:
		offset += data->currentPosition;
		break;
	case 2:
		offset = data->length - offset;
	}
	data->currentPosition = offset;
	return 0;
}

STDMETHODIMP CGflAX::ReceiveBinary(VARIANT newVal)
{
	long
		lLength;
	char *
		pBuffer;

	if (V_ISARRAY(&newVal))
	{
		SafeArrayAccessData(newVal.parray, (void**)&pBuffer);
		lLength = newVal.parray->rgsabound->cElements;
	
		GFL_LOAD_PARAMS
			params;
		gflGetDefaultLoadParams(&params);
		params.Callbacks.Read = MyReadFunction2;
		params.Callbacks.Tell = MyTellFunction2;
		params.Callbacks.Seek = MySeekFunction2;
		params.Flags = GFL_LOAD_SKIP_ALPHA;
		params.Origin = GFL_BOTTOM_LEFT;
		params.ColorModel = GFL_BGR;
		params.LinePadding = 4;
		params.ImageWanted = m_pageIndex-1;

		FreeCurrentBitmap();

		MY_DATA2
			my_data;
		my_data.length = lLength;
		my_data.ptr = (unsigned char *)pBuffer;
		my_data.currentPosition = 0;
		GFL_ERROR
			error = gflLoadBitmapFromHandle(&my_data, &m_bitmap, &params, &m_fileInformations);
	
		SafeArrayUnaccessData(newVal.parray);

		/*
      if (V_ISBYREF(rgvarg))
        pDispatch = *V_DISPATCHREF(rgvarg);
      else
        pDispatch = V_DISPATCH(rgvarg);

      if (pDispatch)
      {
        CComBSTR bstrIOType;
        CComBSTR bstrName;
        CComBSTR bstrTemp;

        CComQIPtr<IDispatch> ptrDisp(pDispatch);
        CComQIPtr<IMagickImage> ptrObject;
        ptrObject = ptrDisp;
        ptrObject->get_Name(&bstrIOType);
        ptrObject->get_Name(&bstrName);
        bstrTemp = bstrIOType;
		*/

		if (error != GFL_NO_ERROR)
		{
			Error(GetErrorString(error));
			return E_FAIL;
		}

		// Keep index of format to use
		m_saveFormatIndex = m_fileInformations.FormatIndex;
	}
	else
	{
		/*
		HRESULT hr = E_FAIL;

		hr = V_DISPATCH(&newVal)->QueryInterface(IID_IADsPropertyValue, (void**)&pValue);
		if (SUCCEEDED(hr))
		{
			hr = pValue->get_ADsType(&lType);
			char string[128];
			sprintf(string, "Test : %d \n", (int)lType);
			Error(string);
			return E_FAIL;
		}
		*/
		char string[128];
		sprintf(string, "Bad parameters : %d \n", (int)newVal.vt);
		Error(string);
		return E_FAIL;
	}

	return S_OK;
}

STDMETHODIMP CGflAX::GetBlob(VARIANT newVal)
{
	return ReceiveBinary(newVal);
}

STDMETHODIMP CGflAX::FreeBitmap()
{
	FreeCurrentBitmap();

	return S_OK;
}

STDMETHODIMP CGflAX::DrawPoint(long x, long y)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor);

	PROCESS2(gflDrawPointColor(m_bitmap, x, y, m_lineWidth, &gfl_line_color, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawLine(long x0, long y0, long x1, long y1)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor);

	PROCESS2(gflDrawLineColor(m_bitmap, x0, y0, x1, y1, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawRectangle(long x, long y, long width, long height)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor);

	PROCESS2(gflDrawRectangleColor(m_bitmap, x, y, width, height, NULL, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawFillRectangle(long x, long y, long width, long height)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor),
		gfl_fill_color = OLEToColor(m_fillColor);

	PROCESS2(gflDrawRectangleColor(m_bitmap, x, y, width, height, &gfl_fill_color, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawCircle(long x, long y, long radius)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor);

	PROCESS2(gflDrawCircleColor(m_bitmap, x, y, radius, NULL, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawFillCircle(long x, long y, long radius)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor),
		gfl_fill_color = OLEToColor(m_fillColor);

	PROCESS2(gflDrawCircleColor(m_bitmap, x, y, radius, &gfl_fill_color, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::AddVertex(long x, long y)
{
	GFL_POINT & vertex = m_vertexArray.push();

	vertex.x = x;
	vertex.y = y;

	return S_OK;
}

STDMETHODIMP CGflAX::FreeVertex()
{
	m_vertexArray.clear();

	return S_OK;
}

STDMETHODIMP CGflAX::DrawPolygon(void)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor),
		gfl_fill_color = OLEToColor(m_fillColor);

	PROCESS2(gflDrawPolygonColor(m_bitmap, m_vertexArray.getPtr(), m_vertexArray.getCount(), &gfl_fill_color, m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::DrawPolyline(void)
{
	GFL_COLOR
		gfl_line_color = OLEToColor(m_lineColor);

	PROCESS2(gflDrawPolylineColor(m_bitmap, m_vertexArray.getPtr(), m_vertexArray.getCount(), m_lineWidth, &gfl_line_color, GFL_LINE_STYLE_SOLID, NULL), 1006);
}

STDMETHODIMP CGflAX::get_LineWidth(long *pVal)
{
	*pVal = m_lineWidth;

	return S_OK;
}

STDMETHODIMP CGflAX::put_LineWidth(long newVal)
{
	m_lineWidth = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_FillColor(OLE_COLOR *pVal)
{
	*pVal = m_fillColor;

	return S_OK;
}

STDMETHODIMP CGflAX::put_FillColor(OLE_COLOR newVal)
{
	m_fillColor = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_LineColor(OLE_COLOR *pVal)
{
	*pVal = m_lineColor;

	return S_OK;
}

STDMETHODIMP CGflAX::put_LineColor(OLE_COLOR newVal)
{
	m_lineColor = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::MergeAddFile(BSTR filename, long opacity, long x, long y)
{
	GFL_MERGE & entry = m_mergeArray.push();

	wcsncpy(entry.filename, filename, MAX_PATH);
	entry.opacity = opacity;
	entry.x = x;
	entry.y = y;

	return S_OK;
}

STDMETHODIMP CGflAX::Merge()
{
	GFL_BITMAP
		** bitmap_list;
	GFL_POINT
		* origin_list;
	GFL_UINT32
		* opacity_list;
	GFL_ERROR
		error = 0;
	int
		i;

	if (m_mergeArray.getCount() < 2)
	{
		Error(GetErrorString(GFL_ERROR_BAD_PARAMETERS));
		return E_FAIL;
	}

	bitmap_list = new GFL_BITMAP *[m_mergeArray.getCount()];
	if (bitmap_list == NULL)
	{
		Error(GetErrorString(GFL_ERROR_NO_MEMORY));
		return E_FAIL;
	}
	origin_list = new GFL_POINT[m_mergeArray.getCount()];
	if (origin_list == NULL)
	{
		delete bitmap_list;
		Error(GetErrorString(GFL_ERROR_NO_MEMORY));
		return E_FAIL;
	}
	opacity_list = new GFL_UINT32[m_mergeArray.getCount()];
	if (opacity_list == NULL)
	{
		delete bitmap_list;
		delete origin_list;
		Error(GetErrorString(GFL_ERROR_NO_MEMORY));
		return E_FAIL;
	}

	for (i=0; i<m_mergeArray.getCount(); i++)
		bitmap_list[i] = NULL;

	bool to_grey = false;
	for (i=0; i<m_mergeArray.getCount(); i++)
	{
		GFL_LOAD_PARAMS
			load_params;
		GFL_BITMAP
			* loaded_bitmap;

		gflGetDefaultLoadParams(&load_params);
		error = gflLoadBitmapW(m_mergeArray[i].filename, &loaded_bitmap, &load_params, NULL);
		if (error)
			break;

		if (i == 0
			&& loaded_bitmap->Type == GFL_GREY)
		{
			gflChangeColorDepth(loaded_bitmap, NULL, GFL_MODE_TO_RGB, GFL_MODE_NO_DITHER);
			to_grey = true;
		}
		else
		if (i
			&& to_grey
			&& loaded_bitmap->ComponentsPerPixel == 4)
			gflChangeColorDepth(loaded_bitmap, NULL, GFL_MODE_TO_RGB, GFL_MODE_NO_DITHER);

		bitmap_list[i] = loaded_bitmap;
		origin_list[i].x = m_mergeArray[i].x;
		origin_list[i].y = m_mergeArray[i].y;
		opacity_list[i] = m_mergeArray[i].opacity;
	}

	if (error)
	{
		for (int i=0; i<m_mergeArray.getCount(); i++)
			if (bitmap_list[i])
				gflFreeBitmap(bitmap_list[i]);

		delete bitmap_list;
		delete origin_list;
		delete opacity_list;
		Error(GetErrorString(error));
		return E_FAIL;
	}

	FreeCurrentBitmap();
	error = gflMerge((const GFL_BITMAP **)bitmap_list, origin_list, opacity_list, m_mergeArray.getCount(), &m_bitmap);

	for (i=0; i<m_mergeArray.getCount(); i++)
		if (bitmap_list[i])
			gflFreeBitmap(bitmap_list[i]);

	delete bitmap_list;
	delete origin_list;
	delete opacity_list;

	if (! error
		&& to_grey)
		gflChangeColorDepth(m_bitmap, NULL, GFL_MODE_TO_256GREY, GFL_MODE_NO_DITHER);

	if (error)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}
	return S_OK;
}

STDMETHODIMP CGflAX::MergeClear()
{
	m_mergeArray.clear();

	return S_OK;
}

STDMETHODIMP CGflAX::Negative()
{
	PROCESS(gflNegative(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Brightness(long value)
{
	PROCESS(gflAdjust(m_bitmap, NULL, value, 0, 1));
}

STDMETHODIMP CGflAX::Contrast(long value)
{
	PROCESS(gflAdjust(m_bitmap, NULL, 0, value, 1));
}

STDMETHODIMP CGflAX::LogCorrection()
{
	PROCESS(gflLogCorrection(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Normalize()
{
	PROCESS(gflNormalize(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Equalize()
{
	PROCESS(gflEqualize(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EqualizeOnLuminance()
{
	PROCESS(gflEqualizeOnLuminance(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Average(long value)
{
	PROCESS(gflAverage(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::Soften(long value)
{
	PROCESS(gflSoften(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::Blur(long value)
{
	PROCESS(gflBlur(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::GaussianBlur(long value)
{
	PROCESS(gflGaussianBlur(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::Maximum(long value)
{
	PROCESS(gflMaximum(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::Minimum(long value)
{
	PROCESS(gflMinimum(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::MedianBox(long value)
{
	PROCESS(gflMedianBox(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::MedianCross(long value)
{
	PROCESS(gflMedianCross(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::Sharpen(long value)
{
	PROCESS(gflSharpen(m_bitmap, NULL, value));
}

STDMETHODIMP CGflAX::EnhanceDetail()
{
	PROCESS(gflEnhanceDetail(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EnhanceFocus()
{
	PROCESS(gflEnhanceFocus(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::FocusRestoration()
{
	PROCESS(gflFocusRestoration(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EdgeDetectLight()
{
	PROCESS(gflEdgeDetectLight(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EdgeDetectMedium()
{
	PROCESS(gflEdgeDetectMedium(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EdgeDetectHeavy()
{
	PROCESS(gflEdgeDetectHeavy(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Emboss()
{
	PROCESS(gflEmboss(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::EmbossMore()
{
	PROCESS(gflEmbossMore(m_bitmap, NULL));
}

STDMETHODIMP CGflAX::Clone(IGflAx **ppImage)
{
	*ppImage = NULL;

	if (! IsImageLoaded())
		return E_FAIL;

	IUnknown
		* pUnk=NULL;

	// Get a COM object to wrap
	HRESULT
		hRes = CoCreateInstance(CLSID_GflAx, NULL, CLSCTX_INPROC_SERVER, IID_IGflAx, (void**) &pUnk);

	if (FAILED(hRes))
		return E_FAIL;

	hRes = pUnk->QueryInterface(IID_IGflAx, (void **) ppImage);
	pUnk->Release(); // because now two references are active
	if (FAILED(hRes))
		return E_FAIL;

	((CGflAX *)(*ppImage))->m_bitmap = gflCloneBitmap(m_bitmap);

	((CGflAX *)(*ppImage))->m_pageIndex = m_pageIndex;
	wcsncpy(((CGflAX *)(*ppImage))->m_bitmapFilename, m_bitmapFilename, MAX_PATH);

	((CGflAX *)(*ppImage))->m_GIFInterlaced = m_GIFInterlaced;
	((CGflAX *)(*ppImage))->m_JPEGProgressive = m_JPEGProgressive;
	((CGflAX *)(*ppImage))->m_JPEGQuality = m_JPEGQuality;
	((CGflAX *)(*ppImage))->m_PNGCompression = m_PNGCompression;
	((CGflAX *)(*ppImage))->m_saveCompression = m_saveCompression;
	((CGflAX *)(*ppImage))->m_saveFormatIndex = m_saveFormatIndex;
	((CGflAX *)(*ppImage))->m_keepMetadata = m_keepMetadata;
	((CGflAX *)(*ppImage))->m_useEmbeddedThumbnail = m_useEmbeddedThumbnail;

	((CGflAX *)(*ppImage))->m_errorLanguage = m_errorLanguage;

	wcsncpy(((CGflAX *)(*ppImage))->m_fontName, m_fontName, LF_FACESIZE);
	((CGflAX *)(*ppImage))->m_fontSize = m_fontSize;
	((CGflAX *)(*ppImage))->m_fontBold = m_fontBold;
	((CGflAX *)(*ppImage))->m_fontItalic = m_fontItalic;
	((CGflAX *)(*ppImage))->m_fontStrikeOut = m_fontStrikeOut;
	((CGflAX *)(*ppImage))->m_fontAntiAlias = m_fontAntiAlias;
	((CGflAX *)(*ppImage))->m_fontUnderline = m_fontUnderline;
	((CGflAX *)(*ppImage))->m_fontOrientation = m_fontOrientation;

	((CGflAX *)(*ppImage))->m_hBitmap = NULL;

	((CGflAX *)(*ppImage))->m_lineWidth = m_lineWidth;
	((CGflAX *)(*ppImage))->m_fillColor = m_fillColor;
	((CGflAX *)(*ppImage))->m_lineColor = m_lineColor;

	((CGflAX *)(*ppImage))->m_useTransparency = m_useTransparency;
	((CGflAX *)(*ppImage))->m_backColor = m_backColor;
	((CGflAX *)(*ppImage))->m_backColor = m_backColor;
	((CGflAX *)(*ppImage))->m_currentZoom = m_currentZoom;
	((CGflAX *)(*ppImage))->m_scaleToGrey = m_scaleToGrey;

	return S_OK;
}

static HBITMAP PrepareMask(HBITMAP hBmpSource, COLORREF clrpTransColor)
{
   BITMAP bm;
	 HDC hdcSrc, hdcDst;
	 HBITMAP hbmSrcT, hbmDstT;
	 HBITMAP hBmpMask;
   COLORREF clrTrans;
	 COLORREF clrSaveBk;
	 COLORREF clrSaveDstText;

   // Get the dimensions of the source bitmap
   GetObject(hBmpSource, sizeof(BITMAP), &bm);

   // Create the mask bitmap
   hBmpMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);

   // We will need two DCs to work with. One to hold the Image
   // (the source), and one to hold the mask (destination).
   // When blitting onto a monochrome bitmap from a color, pixels
   // in the source color bitmap that are equal to the background
   // color are blitted as white. All the remaining pixels are
   // blitted as black.

   hdcSrc = CreateCompatibleDC(NULL);
   hdcDst = CreateCompatibleDC(NULL);

   // Load the bitmaps into memory DC
   hbmSrcT = (HBITMAP)SelectObject(hdcSrc, hBmpSource);
   hbmDstT = (HBITMAP)SelectObject(hdcDst, hBmpMask);

   clrTrans = clrpTransColor;

   // Change the background to trans color
   clrSaveBk  = SetBkColor(hdcSrc, clrTrans);

   // This call sets up the mask bitmap.
   BitBlt(hdcDst, 0,0,bm.bmWidth, bm.bmHeight, hdcSrc,0,0,SRCCOPY);

   // Now, we need to paint onto the original image, making
   // sure that the "transparent" area is set to black. What
   // we do is AND the monochrome image onto the color Image
   // first. When blitting from mono to color, the monochrome
   // pixel is first transformed as follows:
   // if  1 (black) it is mapped to the color set by SetTextColor().
   // if  0 (white) is is mapped to the color set by SetBkColor().
   // Only then is the raster operation performed.

   clrSaveDstText = SetTextColor(hdcSrc, RGB(255,255,255));
   SetBkColor(hdcSrc, RGB(0,0,0));

   BitBlt(hdcSrc, 0,0,bm.bmWidth, bm.bmHeight, hdcDst,0,0,SRCAND);

   // Clean up by deselecting any objects, and delete the
   // DC's.
   SetTextColor(hdcDst, clrSaveDstText);

   SetBkColor(hdcSrc, clrSaveBk);
   SelectObject(hdcSrc, hbmSrcT);
   SelectObject(hdcDst, hbmDstT);

   DeleteDC(hdcSrc);
   DeleteDC(hdcDst);

	 return hBmpMask;
}

static void DrawTransparentBitmap(HDC hDC,
	int xStart, int yStart, int wWidth,  int wHeight,
	HBITMAP hBitmap, HBITMAP hbmpMask,
	int xSource,  int ySource)
{
	HDC hMemoryDC, hOldDC;
	HDC hBmpMemoryDC, hBmpOldDC;

	hBmpMemoryDC = CreateCompatibleDC(hDC);
	hBmpOldDC = (HDC)SelectObject(hBmpMemoryDC, hBitmap);

	hMemoryDC = CreateCompatibleDC(NULL);
	hOldDC = (HDC)SelectObject(hMemoryDC, hbmpMask);

	BitBlt(hDC, xStart, yStart, wWidth, wHeight, hMemoryDC, xSource, ySource, SRCAND);

	BitBlt(hDC, xStart, yStart, wWidth, wHeight, hBmpMemoryDC, xSource, ySource, SRCPAINT);

	SelectObject(hMemoryDC, hOldDC);
	DeleteDC(hMemoryDC);

	SelectObject(hBmpMemoryDC, hBmpOldDC);
	DeleteDC(hBmpMemoryDC);
}

STDMETHODIMP CGflAX::GetPicture(IPicture **ppPicture)
{
	PICTDESC
		pict_desc;
	GFL_ERROR
		error;
	HBITMAP
		hBitmap;

	if (m_currentZoom != 100)
	{
		GFL_BITMAP * tmp_bitmap;
		if (m_currentZoom < 100
			&& m_scaleToGrey)
			gflScaleToGrey(m_bitmap, &tmp_bitmap, m_bitmap->Width * m_currentZoom / 100, m_bitmap->Height * m_currentZoom / 100);
		else
			gflResize(m_bitmap, &tmp_bitmap, m_bitmap->Width * m_currentZoom / 100, m_bitmap->Height * m_currentZoom / 100, GFL_RESIZE_BILINEAR, 0);
		//error = gflConvertBitmapIntoDDB(tmp_bitmap, &hBitmap);
		error = gflConvertBitmapIntoDIBSection(m_bitmap, &hBitmap);
		gflFreeBitmap(tmp_bitmap);
	}
	else
		//error = gflConvertBitmapIntoDDB(m_bitmap, &hBitmap);
		error = gflConvertBitmapIntoDIBSection(m_bitmap, &hBitmap);
	if (error != GFL_NO_ERROR)
		return E_FAIL;

	if (m_useTransparency)
	{
		HBITMAP
			hBmpMask = PrepareMask(hBitmap, m_maskColor);
		HDC
			hDC = GetDC(NULL);
		HBITMAP
			hNewBitmap = CreateCompatibleBitmap(hDC, m_bitmap->Width, m_bitmap->Height);
		HDC
			hMemoryDC = CreateCompatibleDC(hDC);
		HBITMAP
			hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hNewBitmap);
		HBRUSH
			hBrush = CreateSolidBrush(m_backColor);
		RECT
			rect;
		SetRect(&rect, 0, 0, m_bitmap->Width, m_bitmap->Height);
		FillRect(hMemoryDC, &rect, hBrush);
		DeleteObject(hBrush);

		DrawTransparentBitmap(hMemoryDC, 0, 0, m_bitmap->Width, m_bitmap->Height, hBitmap, hBmpMask, 0, 0);

		SelectObject(hMemoryDC, hOldBitmap);
		DeleteDC(hMemoryDC);
		ReleaseDC(NULL, hDC);
	
		DeleteObject(hBitmap);
		DeleteObject(hBmpMask);
		hBitmap = hNewBitmap;
	}

	pict_desc.cbSizeofstruct = sizeof(PICTDESC);
	pict_desc.picType = PICTYPE_BITMAP;
	pict_desc.bmp.hbitmap = hBitmap;

	OleCreatePictureIndirect(&pict_desc, IID_IPicture, TRUE, (void **) ppPicture);

	return S_OK;
}

STDMETHODIMP CGflAX::get_UseTransparency(BOOL *pVal)
{
	*pVal = m_useTransparency;

	return S_OK;
}

STDMETHODIMP CGflAX::put_UseTransparency(BOOL newVal)
{
	m_useTransparency = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_BackColor(OLE_COLOR *pVal)
{
	*pVal = m_backColor;

	return S_OK;
}

STDMETHODIMP CGflAX::put_BackColor(OLE_COLOR newVal)
{
	m_backColor = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_MaskColor(OLE_COLOR *pVal)
{
	*pVal = m_maskColor;

	return S_OK;
}

STDMETHODIMP CGflAX::put_MaskColor(OLE_COLOR newVal)
{
	m_maskColor = newVal;

	if (IsImageLoaded()
		&& (m_bitmap->Type == GFL_COLORS || m_bitmap->Type == GFL_GREY))
	{
		GFL_COLOR color = OLEToColor(m_maskColor);
		int
			min_d2 = 1<<24,
			d2;
		int
			color_index = 0;

		for (int i=0; i<m_bitmap->ColorUsed; i++)
		{
			if (m_bitmap->ColorMap)
			{
				d2 = ((long)m_bitmap->ColorMap->Red[i] - (long)color.Red) * ((long)m_bitmap->ColorMap->Red[i] - (long)color.Red)
					+ ((long)m_bitmap->ColorMap->Green[i] - (long)color.Green) * ((long)m_bitmap->ColorMap->Green[i] - (long)color.Green)
					+ ((long)m_bitmap->ColorMap->Blue[i] - (long)color.Blue) * ((long)m_bitmap->ColorMap->Blue[i] - (long)color.Blue);
			}
			else
			{
				d2 = (i - (long)color.Red) * (i - (long)color.Red)
					+ (i - (long)color.Green) * (i - (long)color.Green)
					+ (i - (long)color.Blue) * (i - (long)color.Blue);
			}
			if (d2 < min_d2)
			{
				min_d2 = d2;
				color_index = i;
			}
		}
		m_bitmap->TransparentIndex = (GFL_INT16)color_index;
	}

	return S_OK;
}

STDMETHODIMP CGflAX::ExportToClipboard()
{
	if (IsImageLoaded())
		PROCESS(gflExportIntoClipboard(m_bitmap));

	return S_OK;
}

STDMETHODIMP CGflAX::ImportFromClipboard()
{
	FreeCurrentBitmap();

	GFL_ERROR
		error;
	if ((error = gflImportFromClipboard(&m_bitmap)) != 0)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	return S_OK;
}

/*
STDMETHODIMP CGflAX::GetFileInfos(BSTR filename, long *pWidth, long *pHeight, BOOL * pRet)
{
	GFL_FILE_INFORMATION info;
	GFL_ERROR error;

	if (error = gflGetFileInformation(W2A(filename), -1, &info))
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}
	*pWidth = info.Width;
	*pHeight = info.Height;

	return S_OK;
}
*/

STDMETHODIMP CGflAX::get_EXIFDateTaken(BSTR *pVal)
{
	CComBSTR bstrResult;
	TCHAR sz[128];

	wsprintf(sz, _T(""));
	if (IsImageLoaded()
		&& gflBitmapHasEXIF(m_bitmap))
	{
		GFL_EXIF_DATA * exif = gflBitmapGetEXIF( m_bitmap, GFL_EXIF_MAIN_IFD|GFL_EXIF_IFD_0 );
		if ( exif )
		{
			for (GFL_UINT32 i=0; i<exif->NumberOfItems; i++)
				if ( exif->ItemsList[i].Tag == 0x9003 )
					wsprintf(sz, _T("%s"), exif->ItemsList[i].Value);
			gflFreeEXIF(exif);
		}
	}
	bstrResult = sz;
	bstrResult.CopyTo(pVal);

	return S_OK;
}

STDMETHODIMP CGflAX::get_EXIFGetByID(unsigned long id, BSTR *pVal)
{
	CComBSTR bstrResult;
	TCHAR sz[128];

	id = id & 0xffff;
	wsprintf(sz, _T(""));
	if (IsImageLoaded()
		&& gflBitmapHasEXIF(m_bitmap))
	{
		GFL_EXIF_DATA * exif = gflBitmapGetEXIF(m_bitmap, GFL_EXIF_MAIN_IFD|GFL_EXIF_IFD_0);
		for (GFL_UINT32 i=0; i<exif->NumberOfItems; i++)
			if (exif->ItemsList[i].Tag == id)
				wsprintf(sz, _T("%s"), exif->ItemsList[i].Value);
		gflFreeEXIF(exif);
	}
	bstrResult = sz;
	bstrResult.CopyTo(pVal);

	return S_OK;
}

STDMETHODIMP CGflAX::SetPicture(IPicture *pict)
{
	short
		type;

	if (pict->get_Type(&type) == S_OK)
	{
		if (type == PICTYPE_BITMAP)
		{
			OLE_HANDLE
				handle;

			if (pict->get_Handle(&handle) == S_OK)
			{
				FreeCurrentBitmap();
				gflConvertDDBIntoBitmap((HBITMAP)handle, &m_bitmap);
			}
		}
	}

	return S_OK;
}

STDMETHODIMP CGflAX::DrawImage(BSTR filename, long x, long y)
{
	GFL_LOAD_PARAMS
		params;
	gflGetDefaultLoadParams(&params);
	params.Flags = 0;
	params.Origin = GFL_BOTTOM_LEFT;
	params.ColorModel = GFL_BGR;
	params.LinePadding = 4;
	params.ImageWanted = m_pageIndex - 1;

	params.EpsWidth = m_epsWidth;
	params.EpsHeight = m_epsHeight;
	params.EpsDpi = (GFL_UINT16)m_epsDpi;

	GFL_ERROR
		error;
	GFL_BITMAP
		* tmp_bitmap;
	error = gflLoadBitmapW(filename, &tmp_bitmap, &params, NULL);
	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	error = gflBitbltEx(tmp_bitmap, NULL, m_bitmap, x, y);
	if (error != GFL_NO_ERROR)
	{
		gflFreeBitmap(tmp_bitmap);
		Error(GetErrorString(error));
		return E_FAIL;
	}
	gflFreeBitmap(tmp_bitmap);

	return S_OK;
}

STDMETHODIMP CGflAX::BeginPaint()
{
	if (m_hBitmap)
		return S_OK;

	if (m_bitmap->BitsPerComponent > 8)
	{
		Error("Bad parameters");
		return E_FAIL;
	}

	GFL_ERROR
		error;
	error = ConvertBitmapIntoDIBSection(m_bitmap, &m_hBitmap, &m_pBits);
	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	return S_OK;
}

STDMETHODIMP CGflAX::EndPaint()
{
	if (! m_hBitmap)
		return S_OK;

	GFL_ERROR
		error;
	error = ConvertDIBSectionIntoBitmap(m_pBits, m_bitmap);
	if (error != GFL_NO_ERROR)
	{
		Error(GetErrorString(error));
		return E_FAIL;
	}

	DeleteObject(m_hBitmap);
	m_hBitmap = NULL;

	return S_OK;
}

STDMETHODIMP CGflAX::put_Xdpi(short newVal)
{
	if (IsImageLoaded())
		m_bitmap->Xdpi = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::put_Ydpi(short newVal)
{
	if (IsImageLoaded())
		m_bitmap->Ydpi = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::get_HasIPTC(BOOL *pVal)
{
	if (IsImageLoaded())
		*pVal = gflBitmapHasIPTC(m_bitmap);

	return S_OK;
}

void CGflAX::GetIPTC(GFL_UINT32 id, BSTR *pRet)
{
    USES_CONVERSION;

	bool found = false;
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			for (GFL_UINT32 i=0; i<iptc_data->NumberOfItems; i++)
				if (iptc_data->ItemsList[i].Id == id)
				{
					*pRet = SysAllocString(A2OLE(iptc_data->ItemsList[i].Value));
					found = true;
					break;
				}

			gflFreeIPTC(iptc_data);
		}
	}
	if (! found)
		*pRet = SysAllocString(L"");
}

/*
#define GFL_IPTC_DATECREATED 							0x37
#define GFL_IPTC_RELEASEDATE 							0x1e
#define GFL_IPTC_TIMECREATED 							0x3c
#define GFL_IPTC_RELEASETIME 							0x23
#define GFL_IPTC_COUNTRYCODE 							0x64
#define GFL_IPTC_SUBLOCATION 							0x5c
#define GFL_IPTC_EDITSTATUS 							0x07
#define GFL_IPTC_PRIORITY 								0x0a
#define GFL_IPTC_OBJECTCYCLE 							0x4b
#define GFL_IPTC_JOBID 										0x16
#define GFL_IPTC_PROGRAM 									0x41
*/

STDMETHODIMP CGflAX::get_IPTCCaption(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_CAPTION, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCaptionWriter(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_CAPTIONWRITER, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCHeadline(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_HEADLINE, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCSpecialinstructions(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_SPECIALINSTRUCTIONS, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCategory(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_CATEGORY, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCByline(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_BYLINE, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCBylineTitle(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_BYLINETITLE, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCredit(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_CREDITS, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCSource(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_SOURCE, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCObjectName(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_OBJECTNAME, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCity(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_CITY, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCProvinceState(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_STATE, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCountryName(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_COUNTRY, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCOTR(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_ORIGINALTRREF, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCCopyrightNotice(BSTR *pVal)
{
	GetIPTC(GFL_IPTC_COPYRIGHT, pVal);
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCDateCreated(DATE *pVal)
{
	BSTR ret;
	GetIPTC(GFL_IPTC_DATECREATED, &ret);
	if (ret)
	{
		int month, day, year;
		WCHAR string[256];
		wscanf(ret, L"%4d%2d%2d", &year, &month, &day);
		_snwprintf(string, 256, L"%02d-%02d-%04d", month, day, year);
		VarDateFromStr(string, 0, VAR_DATEVALUEONLY, pVal);
	}
	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCKeywordsCount(long *pVal)
{
	*pVal = 0;
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			for (GFL_UINT32 i=0; i<iptc_data->NumberOfItems; i++)
			{
				if (iptc_data->ItemsList[i].Id == GFL_IPTC_KEYWORDS)
					(*pVal)++;
			}
		}
	}

	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCKeywords(long index, BSTR *pVal)
{
	bool found = false;

	int cindex=0;
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			for (GFL_UINT32 i=0; i<iptc_data->NumberOfItems; i++)
			{
				if (iptc_data->ItemsList[i].Id == GFL_IPTC_KEYWORDS)
					if (cindex++ == index)
					{
                        USES_CONVERSION;
						*pVal = SysAllocString(A2OLE(iptc_data->ItemsList[i].Value));
						found = true;
						break;
					}
			}
			gflFreeIPTC(iptc_data);
		}
	}

	if (! found)
		*pVal = SysAllocString(L"");

	return S_OK;
}

void CGflAX::SetIPTC(int id, BSTR pVal)
{
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data == NULL)
			iptc_data = gflNewIPTC();
		if (iptc_data)
		{
            USES_CONVERSION;
			gflSetIPTCValue(iptc_data, id, W2A(pVal));
			gflBitmapSetIPTC(m_bitmap, iptc_data);
			gflFreeIPTC(iptc_data);
		}
	}
}

void CGflAX::SetIPTC(int id, const char* pVal)
{
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data == NULL)
			iptc_data = gflNewIPTC();
		if (iptc_data)
		{
			gflSetIPTCValue(iptc_data, id, pVal);
			gflBitmapSetIPTC(m_bitmap, iptc_data);
			gflFreeIPTC(iptc_data);
		}
	}
}

STDMETHODIMP CGflAX::put_IPTCCaption(BSTR value)
{
	SetIPTC(GFL_IPTC_CAPTION, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCaptionWriter(BSTR value)
{
	SetIPTC(GFL_IPTC_CAPTIONWRITER, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCHeadline(BSTR value)
{
	SetIPTC(GFL_IPTC_HEADLINE, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCSpecialinstructions(BSTR value)
{
	SetIPTC(GFL_IPTC_SPECIALINSTRUCTIONS, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCategory(BSTR value)
{
	SetIPTC(GFL_IPTC_CATEGORY, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCByline(BSTR value)
{
	SetIPTC(GFL_IPTC_BYLINE, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCBylineTitle(BSTR value)
{
	SetIPTC(GFL_IPTC_BYLINETITLE, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCredit(BSTR value)
{
	SetIPTC(GFL_IPTC_CREDITS, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCSource(BSTR value)
{
	SetIPTC(GFL_IPTC_SOURCE, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCObjectName(BSTR value)
{
	SetIPTC(GFL_IPTC_OBJECTNAME, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCity(BSTR value)
{
	SetIPTC(GFL_IPTC_CITY, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCProvinceState(BSTR value)
{
	SetIPTC(GFL_IPTC_STATE, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCountryName(BSTR value)
{
	SetIPTC(GFL_IPTC_COUNTRY, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCOTR(BSTR value)
{
	SetIPTC(GFL_IPTC_ORIGINALTRREF, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCCopyrightNotice(BSTR value)
{
	SetIPTC(GFL_IPTC_COPYRIGHT, value);
	return S_OK;
}

STDMETHODIMP CGflAX::IPTCKeywordsAdd(BSTR value)
{
	SetIPTC(GFL_IPTC_KEYWORDS, value);
	return S_OK;
}

STDMETHODIMP CGflAX::IPTCSuppCatAdd(BSTR value)
{
	SetIPTC(GFL_IPTC_SUPCATEGORIES, value);
	return S_OK;
}

STDMETHODIMP CGflAX::put_IPTCDateCreated(DATE pVal)
{
	SYSTEMTIME systime;
	VariantTimeToSystemTime(pVal, &systime);
	//VarBstrFromDate(0.0, 0, 0, &ret);
	char string[64];
	_snprintf(string, 64, "%04d%02d%02d", systime.wYear, systime.wMonth, systime.wDay);
	SetIPTC(GFL_IPTC_DATECREATED, string);

	return S_OK;
}

STDMETHODIMP CGflAX::IPTCKeywordsClear()
{
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			gflClearIPTCKeywords(iptc_data);
			gflBitmapSetIPTC(m_bitmap, iptc_data);
			gflFreeIPTC(iptc_data);
		}
	}

	return S_OK;
}

STDMETHODIMP CGflAX::IPTCKeywordsDelete(long index)
{
    UNREFERENCED_PARAMETER(index);

	// TODO: Add your implementation code here

	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCSuppCatCount(long *pVal)
{
	*pVal = 0;
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			for (GFL_UINT32 i=0; i<iptc_data->NumberOfItems; i++)
			{
				if (iptc_data->ItemsList[i].Id == GFL_IPTC_SUPCATEGORIES)
					(*pVal)++;
			}
		}
	}

	return S_OK;
}

STDMETHODIMP CGflAX::get_IPTCSuppCat(long index, BSTR *pVal)
{
	bool found = false;
	int cindex=0;
	if (IsImageLoaded())
	{
		GFL_IPTC_DATA * iptc_data = gflBitmapGetIPTC(m_bitmap);
		if (iptc_data)
		{
			for (GFL_UINT32 i=0; i<iptc_data->NumberOfItems; i++)
			{
				if (iptc_data->ItemsList[i].Id == GFL_IPTC_SUPCATEGORIES)
					if (cindex++ == index)
					{
                        USES_CONVERSION;
						*pVal = SysAllocString(A2OLE(iptc_data->ItemsList[i].Value));
						found = true;
						break;
					}
			}
			gflFreeIPTC(iptc_data);
		}
	}

	if (! found)
		*pVal = SysAllocString(L"");

	return S_OK;
}

STDMETHODIMP CGflAX::IPTCSuppCatClear()
{
	// TODO: Add your implementation code here

	return S_OK;
}
STDMETHODIMP CGflAX::IPTCSuppCatDelete(long index)
{
    UNREFERENCED_PARAMETER(index);

    // TODO: Add your implementation code here

	return S_OK;
}


STDMETHODIMP CGflAX::GetTextWidth(BSTR string, long *pVal)
{
	SIZE size;
	GetTextExtent(string, &size);
	*pVal = size.cx;

	return S_OK;
}

STDMETHODIMP CGflAX::GetTextHeight(BSTR string, long *pVal)
{
	SIZE size;
	GetTextExtent(string, &size);
	*pVal = size.cy;

	return S_OK;
}

void CGflAX::GetTextExtent(BSTR str, SIZE *size)
{
	HDC hDC;
	hDC = GetDC(NULL);

	LOGFONTW log_font;
	memset(&log_font, 0, sizeof(log_font));
	log_font.lfHeight = m_fontSize;
	log_font.lfWidth = 0;
	log_font.lfEscapement = m_fontOrientation * 10;
	log_font.lfOrientation = 0;
	log_font.lfWeight = m_fontBold ? FW_BOLD : FW_NORMAL;
	log_font.lfItalic = m_fontItalic ? TRUE : FALSE;
	log_font.lfUnderline = m_fontUnderline ? TRUE : FALSE;
	log_font.lfStrikeOut = m_fontStrikeOut ? TRUE : FALSE;
	log_font.lfOutPrecision = OUT_DEFAULT_PRECIS;
	log_font.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	log_font.lfQuality = (m_fontAntiAlias ? ANTIALIASED_QUALITY : PROOF_QUALITY);
	log_font.lfCharSet = DEFAULT_CHARSET;
	log_font.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
	wcsncpy(log_font.lfFaceName, m_fontName, LF_FACESIZE);
	
	HFONT hFont = CreateFontIndirectW(&log_font);
	HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);

	RECT rect;
	memset(&rect, 0, sizeof(rect));
	DrawTextW(hDC, str, (int)wcslen(str), &rect, DT_CALCRECT | DT_NOPREFIX);

	SelectObject(hDC, hOldFont);
	DeleteObject(hFont);

	ReleaseDC(NULL, hDC);

	size->cx = rect.right-rect.left;
	size->cy = rect.bottom-rect.top;
}

#ifdef __TWAIN_SUPPORT__

STDMETHODIMP CGflAX::SelectTwainDevice()
{
	TWAINSelectSource(FALSE);
	return S_OK;
}

STDMETHODIMP CGflAX::get_CurrentTwainDevice(long *pVal)
{
	TWAINSelectSource(TRUE);
	return S_OK;
}

STDMETHODIMP CGflAX::get_TwainDeviceName(BSTR *pVal)
{
	// TODO: Add your implementation code here

	return S_OK;
}

STDMETHODIMP CGflAX::get_TwainConnected(BOOL *pVal)
{
	*pVal = TWAINIsOpen();

	return S_OK;
}

STDMETHODIMP CGflAX::get_TwainAppName(BSTR *pVal)
{
	// TODO: Add your implementation code here

	return S_OK;
}

STDMETHODIMP CGflAX::put_TwainAppName(BSTR newVal)
{
	if (TWAINIsOpen())
		TWAINClose();
	TWAINInitialize(NULL, W2A(newVal));

	return S_OK;
}

STDMETHODIMP CGflAX::get_UseTwainInterface(BOOL *pVal)
{
	// TODO: Add your implementation code here

	return S_OK;
}

STDMETHODIMP CGflAX::put_UseTwainInterface(BOOL newVal)
{
	// TODO: Add your implementation code here

	return S_OK;
}

STDMETHODIMP CGflAX::Acquire()
{
	INT x_dpi, y_dpi;
	HANDLE hDIB;
	if (TWAINAcquire(TRUE, &hDIB, &x_dpi, &y_dpi))
	{
		FreeCurrentBitmap();
		gflConvertDIBIntoBitmap(hDIB, &m_bitmap);
		if (m_bitmap->Type == GFL_BINARY)
			m_bitmap->ColorUsed = 2;
		GlobalFree(hDIB);
	}

	if (TWAINIsOpen())
		TWAINClose();

	return S_OK;
}

#endif

STDMETHODIMP CGflAX::get_Zoom(long *pVal)
{
	*pVal = m_currentZoom;
	return S_OK;
}

STDMETHODIMP CGflAX::put_Zoom(long newVal)
{
	if (newVal >= 5
		&& newVal <= 5000)
		m_currentZoom = newVal;
	return S_OK;
}

STDMETHODIMP CGflAX::get_ScaleToGrey(BOOL *pVal)
{
	*pVal = m_scaleToGrey;
	return S_OK;
}

STDMETHODIMP CGflAX::put_ScaleToGrey(BOOL newVal)
{
	m_scaleToGrey = newVal;
	return S_OK;
}

long CGflAX::GetCompression(long index)
{
	if (index == gflGetFormatIndexByName("tiff"))
	{
		switch(m_saveCompression)
		{
		case AX_NoCompression:
			return GFL_NO_COMPRESSION;
		case AX_RLE:
			return GFL_RLE;
		case AX_LZW:
			return GFL_LZW;
		case AX_ZIP:
			return GFL_ZIP;
		case AX_G3:
			return GFL_CCITT_FAX3;
		case AX_G32D:
			return GFL_CCITT_FAX3_2D;
		case AX_G4:
			return GFL_CCITT_FAX4;
		}
	}

	return m_saveCompression == AX_NoCompression ? 0 : 1;
}

STDMETHODIMP CGflAX::get_UseDPIFromEXIF(BOOL *pVal)
{
	*pVal = m_useEXIFDPI;

	return S_OK;
}

STDMETHODIMP CGflAX::put_UseDPIFromEXIF(BOOL newVal)
{
	m_useEXIFDPI = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::put_UseEmbeddedThumbnail(BOOL newVal)
{
	m_useEmbeddedThumbnail = newVal;

	return S_OK;
}

STDMETHODIMP CGflAX::put_UpdateEmbeddedThumbnail(BOOL newVal)
{
	m_updateEmbeddedThumbnail = newVal;

	return S_OK;
}

#include <winsock.h>
#include <wininet.h>

GFL_UINT8 * CGflAX::UploadFilename(BSTR url, DWORD * length)
{
	DWORD dwFlags;
	HINTERNET hInternetSession;

	if (! InternetGetConnectedState(&dwFlags, 0))
	{
		// Prompt the user to connect to the internet.
		if (! InternetAutodial(0, 0))
			return NULL;
	}

	hInternetSession = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (hInternetSession == NULL)
		return FALSE;

	DWORD dwBytesRead = 0;
	HINTERNET hURL;

	hURL = InternetOpenUrlW(hInternetSession, url, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0, 0);
	if (hURL == NULL)
	{
		InternetCloseHandle(hInternetSession);
		return NULL;
	}

	*length = 0;
	GFL_UINT8 * buffer = NULL;
	do
	{
		if (buffer == NULL)
			buffer = (GFL_UINT8 *)malloc(*length+32000);
		else
			buffer = (GFL_UINT8 *)realloc(buffer, *length+32000);
	
        if (buffer) {
			InternetReadFile(hURL, (LPSTR)buffer+*length, (DWORD)32000, &dwBytesRead);
		    *length += dwBytesRead;
        }
	}
	while (dwBytesRead > 0);

	// Close the handle to the URL.
	InternetCloseHandle(hURL);

	// Close down connections.
	InternetCloseHandle(hInternetSession);

	return buffer;
}
