Internal change

PiperOrigin-RevId: 355864554
Change-Id: I6eb917373c744424cd71f195b8e898c9bbd7bcc7
diff --git a/files/Source/CacheFile.h b/files/Source/CacheFile.h
new file mode 100644
index 0000000..a1e5e78
--- /dev/null
+++ b/files/Source/CacheFile.h
@@ -0,0 +1,92 @@
+// ==========================================================
+// Multi-Page functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef CACHEFILE_H
+#define CACHEFILE_H
+
+// ----------------------------------------------------------
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+static const int CACHE_SIZE = 32;
+static const int BLOCK_SIZE = (64 * 1024) - 8;
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // _WIN32
+
+struct Block {
+     unsigned nr;
+     unsigned next;
+     BYTE *data;
+};
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // _WIN32
+
+// ----------------------------------------------------------
+
+class CacheFile {
+	typedef std::list<Block *> PageCache;
+	typedef std::list<Block *>::iterator PageCacheIt;
+	typedef std::map<int, PageCacheIt> PageMap;
+	typedef std::map<int, PageCacheIt>::iterator PageMapIt;
+
+public :
+	CacheFile(const std::string filename, BOOL keep_in_memory);
+	~CacheFile();
+
+	BOOL open();
+	void close();
+	BOOL readFile(BYTE *data, int nr, int size);
+	int writeFile(BYTE *data, int size);
+	void deleteFile(int nr);
+
+private :
+	void cleanupMemCache();
+	int allocateBlock();
+	Block *lockBlock(int nr);
+	BOOL unlockBlock(int nr);
+	BOOL deleteBlock(int nr);
+
+private :
+	FILE *m_file;
+	std::string m_filename;
+	std::list<int> m_free_pages;
+	PageCache m_page_cache_mem;
+	PageCache m_page_cache_disk;
+	PageMap m_page_map;
+	int m_page_count;
+	Block *m_current_block;
+	BOOL m_keep_in_memory;
+};
+
+#endif // CACHEFILE_H
diff --git a/files/Source/DeprecationManager/Deprecated.cpp b/files/Source/DeprecationManager/Deprecated.cpp
new file mode 100644
index 0000000..8ca5e71
--- /dev/null
+++ b/files/Source/DeprecationManager/Deprecated.cpp
@@ -0,0 +1,36 @@
+// ==========================================================
+// Deprecated functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+
+#include "../DeprecationManager/DeprecationMgr.h"
+
+// ----------------------------------------------------------
+
+FIBITMAP *DLL_CALLCONV 
+FreeImage_RotateClassic(FIBITMAP *dib, double angle) {
+#ifdef _WIN32
+	DEPRECATE("FreeImage_RotateClassic()", "FreeImage_Rotate()")
+#endif // _WIN32
+	return FreeImage_Rotate(dib, angle, NULL);
+}
+
diff --git a/files/Source/DeprecationManager/DeprecationMgr.cpp b/files/Source/DeprecationManager/DeprecationMgr.cpp
new file mode 100644
index 0000000..bf82dfa
--- /dev/null
+++ b/files/Source/DeprecationManager/DeprecationMgr.cpp
@@ -0,0 +1,103 @@
+// ==========================================================
+// Deprecation Manager
+//
+// Design and implementation by
+// - Noel Llopis (Game Programming Gems II)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif 
+
+#ifdef _WIN32
+#include <windows.h>
+#endif // _WIN32
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "DeprecationMgr.h"
+
+// ==========================================================
+
+DeprecationMgr::DeprecationMgr() {
+}
+
+DeprecationMgr::~DeprecationMgr() {
+#ifdef _WIN32
+	if (!m_functions.empty()) {
+		OutputDebugString( "*************************************************************************************\n" );
+		OutputDebugString( "This is a warning, because you use one or more deprecated functions.\nContinuing to use these functions might eventually render your program uncompilable.\nThe following functions are deprecated:\n\n" );
+
+		for (std::map<const char *, DeprecatedFunction>::iterator i = m_functions.begin(); i != m_functions.end(); ++i) {
+			DeprecatedFunction *function = &((*i).second);
+
+			char txt[255];
+
+			sprintf(txt, " * %s called from %i different places. Instead use %s.\n", function->old_function_name,  function->called_from.size(), function->new_function_name);
+
+			OutputDebugString(txt);
+		}
+
+		OutputDebugString( "*************************************************************************************\n" );
+
+		m_functions.clear();
+	}
+#endif // _WIN32
+}
+
+// ==========================================================
+
+DeprecationMgr *
+DeprecationMgr::GetInstance() {
+	static DeprecationMgr Instance;
+	return &Instance;
+}
+
+// ==========================================================
+
+void
+DeprecationMgr::AddDeprecatedFunction(const char *old_function_name, const char *new_function_name, const void *frame_ptr) {
+#ifdef _WIN32
+	int *preturn = (int *)frame_ptr + 1; // usual return address @ [ebp+4]
+	int called_from = IsBadReadPtr(preturn, 4) ? 0 : *preturn;
+
+	// check if this function was already listed as deprecated
+	// if it wasn't, make a new entry for it
+	// if it was, keep track of where it's called from.
+
+	std::map<const char *, DeprecatedFunction>::iterator existing_function = m_functions.find(old_function_name);
+
+	if (existing_function == m_functions.end()) {
+		DeprecatedFunction function;
+
+		function.old_function_name = old_function_name;
+		function.new_function_name = new_function_name;
+		function.called_from.insert(called_from);
+
+		m_functions[old_function_name] = function;
+	} else {
+		// since we're keeping track of the addresses this function
+		// was called from in a set, we don't need to check whether we've
+		// already added the address.
+
+		DeprecatedFunction *function = &((*existing_function).second);
+
+		function->called_from.insert(called_from);
+	}
+#endif // _WIN32
+}
+
+
diff --git a/files/Source/DeprecationManager/DeprecationMgr.h b/files/Source/DeprecationManager/DeprecationMgr.h
new file mode 100644
index 0000000..9141f23
--- /dev/null
+++ b/files/Source/DeprecationManager/DeprecationMgr.h
@@ -0,0 +1,83 @@
+// ==========================================================
+// Deprecation Manager
+//
+// Design and implementation by
+// - Noel Llopis (Game Programming Gems II)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef DEPRECATIONMGR_H
+#define DEPRECATIONMGR_H
+
+#ifdef _MSC_VER 
+#pragma warning(disable : 4786 )  // identifier was truncated to 'number' characters
+#endif 
+
+#include "Utilities.h"
+
+// ==========================================================
+
+#if !defined(_M_X64) && defined(_MSC_VER)
+	#define DEPRECATE(a,b) \
+	{ \
+		void *fptr;	\
+		_asm { mov fptr, ebp }	\
+		DeprecationMgr::GetInstance()->AddDeprecatedFunction(a, b, fptr); \
+	}
+
+#elif defined(__i386__) && defined(__GNUC__)
+	#define DEPRECATE(a,b) \
+	{ \
+		void *fptr;	\
+		__asm__("movl %%ebp, %0" : "=m" (fptr));	\
+		DeprecationMgr::GetInstance()->AddDeprecatedFunction(a, b, fptr); \
+	}
+
+#else
+	// default fallback case, which does not use the ebp register's content
+	#define DEPRECATE(a,b) \
+	{ \
+		void *fptr = NULL;	\
+		DeprecationMgr::GetInstance()->AddDeprecatedFunction(a, b, fptr); \
+	}
+#endif
+
+// ==========================================================
+
+class DeprecationMgr {
+#if (_MSC_VER == 1100) // VC 5.0 need to look into the docs for the compiler for the value of each version
+public:
+#else
+private:
+#endif
+
+	struct DeprecatedFunction {
+		const char *old_function_name;
+		const char *new_function_name;
+		std::set<int> called_from;
+	};
+
+	std::map<const char *, DeprecatedFunction> m_functions;
+
+public:
+	DeprecationMgr();
+	~DeprecationMgr();
+
+	static DeprecationMgr * GetInstance ( void );
+	void AddDeprecatedFunction(const char *old_function_name, const char *new_function_name, const void *frame_ptr);
+};
+
+#endif //DEPRECATIONMGR_H
diff --git a/files/Source/FreeImage.h b/files/Source/FreeImage.h
new file mode 100644
index 0000000..a8eb5de
--- /dev/null
+++ b/files/Source/FreeImage.h
@@ -0,0 +1,1152 @@
+// ==========================================================
+// FreeImage 3
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// Contributors:
+// - see changes log named 'Whatsnew.txt', see header of each .h and .cpp file
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef FREEIMAGE_H
+#define FREEIMAGE_H
+
+// Version information ------------------------------------------------------
+
+#define FREEIMAGE_MAJOR_VERSION   3
+#define FREEIMAGE_MINOR_VERSION   17
+#define FREEIMAGE_RELEASE_SERIAL  0
+
+// Compiler options ---------------------------------------------------------
+
+#include <wchar.h>	// needed for UNICODE functions
+
+#if defined(FREEIMAGE_LIB)
+	#define DLL_API
+	#define DLL_CALLCONV
+#else
+	#if defined(_WIN32) || defined(__WIN32__)
+		#define DLL_CALLCONV __stdcall
+		// The following ifdef block is the standard way of creating macros which make exporting 
+		// from a DLL simpler. All files within this DLL are compiled with the FREEIMAGE_EXPORTS
+		// symbol defined on the command line. this symbol should not be defined on any project
+		// that uses this DLL. This way any other project whose source files include this file see 
+		// DLL_API functions as being imported from a DLL, wheras this DLL sees symbols
+		// defined with this macro as being exported.
+		#ifdef FREEIMAGE_EXPORTS
+			#define DLL_API __declspec(dllexport)
+		#else
+			#define DLL_API __declspec(dllimport)
+		#endif // FREEIMAGE_EXPORTS
+	#else 
+		// try the gcc visibility support (see http://gcc.gnu.org/wiki/Visibility)
+		#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+			#ifndef GCC_HASCLASSVISIBILITY
+				#define GCC_HASCLASSVISIBILITY
+			#endif
+		#endif // __GNUC__
+		#define DLL_CALLCONV
+		#if defined(GCC_HASCLASSVISIBILITY)
+			#define DLL_API __attribute__ ((visibility("default")))
+		#else
+			#define DLL_API
+		#endif		
+	#endif // WIN32 / !WIN32
+#endif // FREEIMAGE_LIB
+
+// Endianness:
+// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined.
+// If your big endian system isn't being detected, add an OS specific check
+// or define any of FREEIMAGE_BIGENDIAN and FREEIMAGE_LITTLEENDIAN directly
+// to specify the desired endianness.
+#if (!defined(FREEIMAGE_BIGENDIAN) && !defined(FREEIMAGE_LITTLEENDIAN))
+	#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || defined(__BIG_ENDIAN__)
+		#define FREEIMAGE_BIGENDIAN
+	#endif // BYTE_ORDER
+#endif // !FREEIMAGE_[BIG|LITTLE]ENDIAN
+
+// Color-Order:
+// The specified order of color components red, green and blue affects 24-
+// and 32-bit images of type FIT_BITMAP as well as the colors that are part
+// of a color palette. All other images always use RGB order. By default,
+// color order is coupled to endianness:
+// little-endian -> BGR
+// big-endian    -> RGB
+// However, you can always define FREEIMAGE_COLORORDER to any of the known
+// orders FREEIMAGE_COLORORDER_BGR (0) and FREEIMAGE_COLORORDER_RGB (1) to
+// specify your preferred color order.
+#define FREEIMAGE_COLORORDER_BGR    0
+#define FREEIMAGE_COLORORDER_RGB    1
+#if (!defined(FREEIMAGE_COLORORDER)) || ((FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_BGR) && (FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_RGB))
+	#if defined(FREEIMAGE_BIGENDIAN)
+		#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_RGB
+	#else
+		#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR
+	#endif // FREEIMAGE_BIGENDIAN
+#endif // FREEIMAGE_COLORORDER
+
+// Ensure 4-byte enums if we're using Borland C++ compilers
+#if defined(__BORLANDC__)
+#pragma option push -b
+#endif
+
+// For C compatibility --------------------------------------------------------
+
+#ifdef __cplusplus
+#define FI_DEFAULT(x)	= x
+#define FI_ENUM(x)      enum x
+#define FI_STRUCT(x)	struct x
+#else
+#define FI_DEFAULT(x)
+#define FI_ENUM(x)      typedef int x; enum x
+#define FI_STRUCT(x)	typedef struct x x; struct x
+#endif
+
+// Bitmap types -------------------------------------------------------------
+
+FI_STRUCT (FIBITMAP) { void *data; };
+FI_STRUCT (FIMULTIBITMAP) { void *data; };
+
+// Types used in the library (directly copied from Windows) -----------------
+
+#if defined(__MINGW32__) && defined(_WINDOWS_H)
+#define _WINDOWS_	// prevent a bug in MinGW32
+#endif // __MINGW32__
+
+#ifndef _WINDOWS_
+#define _WINDOWS_
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET  0
+#define SEEK_CUR  1
+#define SEEK_END  2
+#endif
+
+#ifndef _MSC_VER
+// define portable types for 32-bit / 64-bit OS
+#include <inttypes.h>
+typedef int32_t BOOL;
+typedef uint8_t BYTE;
+typedef uint16_t WORD;
+typedef uint32_t DWORD;
+typedef int32_t LONG;
+typedef int64_t INT64;
+typedef uint64_t UINT64;
+#else
+// MS is not C99 ISO compliant
+typedef long BOOL;
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+typedef long LONG;
+typedef signed __int64 INT64;
+typedef unsigned __int64 UINT64;
+#endif // _MSC_VER
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+typedef struct tagRGBQUAD {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+  BYTE rgbBlue;
+  BYTE rgbGreen;
+  BYTE rgbRed;
+#else
+  BYTE rgbRed;
+  BYTE rgbGreen;
+  BYTE rgbBlue;
+#endif // FREEIMAGE_COLORORDER
+  BYTE rgbReserved;
+} RGBQUAD;
+
+typedef struct tagRGBTRIPLE {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+  BYTE rgbtBlue;
+  BYTE rgbtGreen;
+  BYTE rgbtRed;
+#else
+  BYTE rgbtRed;
+  BYTE rgbtGreen;
+  BYTE rgbtBlue;
+#endif // FREEIMAGE_COLORORDER
+} RGBTRIPLE;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+typedef struct tagBITMAPINFOHEADER{
+  DWORD biSize;
+  LONG  biWidth; 
+  LONG  biHeight; 
+  WORD  biPlanes; 
+  WORD  biBitCount;
+  DWORD biCompression; 
+  DWORD biSizeImage; 
+  LONG  biXPelsPerMeter; 
+  LONG  biYPelsPerMeter; 
+  DWORD biClrUsed; 
+  DWORD biClrImportant;
+} BITMAPINFOHEADER, *PBITMAPINFOHEADER; 
+
+typedef struct tagBITMAPINFO { 
+  BITMAPINFOHEADER bmiHeader; 
+  RGBQUAD          bmiColors[1];
+} BITMAPINFO, *PBITMAPINFO;
+
+#endif // _WINDOWS_
+
+// Types used in the library (specific to FreeImage) ------------------------
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+/** 48-bit RGB 
+*/
+typedef struct tagFIRGB16 {
+	WORD red;
+	WORD green;
+	WORD blue;
+} FIRGB16;
+
+/** 64-bit RGBA
+*/
+typedef struct tagFIRGBA16 {
+	WORD red;
+	WORD green;
+	WORD blue;
+	WORD alpha;
+} FIRGBA16;
+
+/** 96-bit RGB Float
+*/
+typedef struct tagFIRGBF {
+	float red;
+	float green;
+	float blue;
+} FIRGBF;
+
+/** 128-bit RGBA Float
+*/
+typedef struct tagFIRGBAF {
+	float red;
+	float green;
+	float blue;
+	float alpha;
+} FIRGBAF;
+
+/** Data structure for COMPLEX type (complex number)
+*/
+typedef struct tagFICOMPLEX {
+    /// real part
+	double r;
+	/// imaginary part
+    double i;
+} FICOMPLEX;
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+// Indexes for byte arrays, masks and shifts for treating pixels as words ---
+// These coincide with the order of RGBQUAD and RGBTRIPLE -------------------
+
+#ifndef FREEIMAGE_BIGENDIAN
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+// Little Endian (x86 / MS Windows, Linux) : BGR(A) order
+#define FI_RGBA_RED				2
+#define FI_RGBA_GREEN			1
+#define FI_RGBA_BLUE			0
+#define FI_RGBA_ALPHA			3
+#define FI_RGBA_RED_MASK		0x00FF0000
+#define FI_RGBA_GREEN_MASK		0x0000FF00
+#define FI_RGBA_BLUE_MASK		0x000000FF
+#define FI_RGBA_ALPHA_MASK		0xFF000000
+#define FI_RGBA_RED_SHIFT		16
+#define FI_RGBA_GREEN_SHIFT		8
+#define FI_RGBA_BLUE_SHIFT		0
+#define FI_RGBA_ALPHA_SHIFT		24
+#else
+// Little Endian (x86 / MaxOSX) : RGB(A) order
+#define FI_RGBA_RED				0
+#define FI_RGBA_GREEN			1
+#define FI_RGBA_BLUE			2
+#define FI_RGBA_ALPHA			3
+#define FI_RGBA_RED_MASK		0x000000FF
+#define FI_RGBA_GREEN_MASK		0x0000FF00
+#define FI_RGBA_BLUE_MASK		0x00FF0000
+#define FI_RGBA_ALPHA_MASK		0xFF000000
+#define FI_RGBA_RED_SHIFT		0
+#define FI_RGBA_GREEN_SHIFT		8
+#define FI_RGBA_BLUE_SHIFT		16
+#define FI_RGBA_ALPHA_SHIFT		24
+#endif // FREEIMAGE_COLORORDER
+#else
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+// Big Endian (PPC / none) : BGR(A) order
+#define FI_RGBA_RED				2
+#define FI_RGBA_GREEN			1
+#define FI_RGBA_BLUE			0
+#define FI_RGBA_ALPHA			3
+#define FI_RGBA_RED_MASK		0x0000FF00
+#define FI_RGBA_GREEN_MASK		0x00FF0000
+#define FI_RGBA_BLUE_MASK		0xFF000000
+#define FI_RGBA_ALPHA_MASK		0x000000FF
+#define FI_RGBA_RED_SHIFT		8
+#define FI_RGBA_GREEN_SHIFT		16
+#define FI_RGBA_BLUE_SHIFT		24
+#define FI_RGBA_ALPHA_SHIFT		0
+#else
+// Big Endian (PPC / Linux, MaxOSX) : RGB(A) order
+#define FI_RGBA_RED				0
+#define FI_RGBA_GREEN			1
+#define FI_RGBA_BLUE			2
+#define FI_RGBA_ALPHA			3
+#define FI_RGBA_RED_MASK		0xFF000000
+#define FI_RGBA_GREEN_MASK		0x00FF0000
+#define FI_RGBA_BLUE_MASK		0x0000FF00
+#define FI_RGBA_ALPHA_MASK		0x000000FF
+#define FI_RGBA_RED_SHIFT		24
+#define FI_RGBA_GREEN_SHIFT		16
+#define FI_RGBA_BLUE_SHIFT		8
+#define FI_RGBA_ALPHA_SHIFT		0
+#endif // FREEIMAGE_COLORORDER
+#endif // FREEIMAGE_BIGENDIAN
+
+#define FI_RGBA_RGB_MASK		(FI_RGBA_RED_MASK|FI_RGBA_GREEN_MASK|FI_RGBA_BLUE_MASK)
+
+// The 16bit macros only include masks and shifts, since each color element is not byte aligned
+
+#define FI16_555_RED_MASK		0x7C00
+#define FI16_555_GREEN_MASK		0x03E0
+#define FI16_555_BLUE_MASK		0x001F
+#define FI16_555_RED_SHIFT		10
+#define FI16_555_GREEN_SHIFT	5
+#define FI16_555_BLUE_SHIFT		0
+#define FI16_565_RED_MASK		0xF800
+#define FI16_565_GREEN_MASK		0x07E0
+#define FI16_565_BLUE_MASK		0x001F
+#define FI16_565_RED_SHIFT		11
+#define FI16_565_GREEN_SHIFT	5
+#define FI16_565_BLUE_SHIFT		0
+
+// ICC profile support ------------------------------------------------------
+
+#define FIICC_DEFAULT			0x00
+#define FIICC_COLOR_IS_CMYK		0x01
+
+FI_STRUCT (FIICCPROFILE) { 
+	WORD    flags;	//! info flag
+	DWORD	size;	//! profile's size measured in bytes
+	void   *data;	//! points to a block of contiguous memory containing the profile
+};
+
+// Important enums ----------------------------------------------------------
+
+/** I/O image format identifiers.
+*/
+FI_ENUM(FREE_IMAGE_FORMAT) {
+	FIF_UNKNOWN = -1,
+	FIF_BMP		= 0,
+	FIF_ICO		= 1,
+	FIF_JPEG	= 2,
+	FIF_JNG		= 3,
+	FIF_KOALA	= 4,
+	FIF_LBM		= 5,
+	FIF_IFF = FIF_LBM,
+	FIF_MNG		= 6,
+	FIF_PBM		= 7,
+	FIF_PBMRAW	= 8,
+	FIF_PCD		= 9,
+	FIF_PCX		= 10,
+	FIF_PGM		= 11,
+	FIF_PGMRAW	= 12,
+	FIF_PNG		= 13,
+	FIF_PPM		= 14,
+	FIF_PPMRAW	= 15,
+	FIF_RAS		= 16,
+	FIF_TARGA	= 17,
+	FIF_TIFF	= 18,
+	FIF_WBMP	= 19,
+	FIF_PSD		= 20,
+	FIF_CUT		= 21,
+	FIF_XBM		= 22,
+	FIF_XPM		= 23,
+	FIF_DDS		= 24,
+	FIF_GIF     = 25,
+	FIF_HDR		= 26,
+	FIF_FAXG3	= 27,
+	FIF_SGI		= 28,
+	FIF_EXR		= 29,
+	FIF_J2K		= 30,
+	FIF_JP2		= 31,
+	FIF_PFM		= 32,
+	FIF_PICT	= 33,
+	FIF_RAW		= 34,
+	FIF_WEBP	= 35,
+};
+
+/** Image type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_TYPE) {
+	FIT_UNKNOWN = 0,	//! unknown type
+	FIT_BITMAP  = 1,	//! standard image			: 1-, 4-, 8-, 16-, 24-, 32-bit
+	FIT_UINT16	= 2,	//! array of unsigned short	: unsigned 16-bit
+	FIT_INT16	= 3,	//! array of short			: signed 16-bit
+	FIT_UINT32	= 4,	//! array of unsigned long	: unsigned 32-bit
+	FIT_INT32	= 5,	//! array of long			: signed 32-bit
+	FIT_FLOAT	= 6,	//! array of float			: 32-bit IEEE floating point
+	FIT_DOUBLE	= 7,	//! array of double			: 64-bit IEEE floating point
+	FIT_COMPLEX	= 8,	//! array of FICOMPLEX		: 2 x 64-bit IEEE floating point
+	FIT_RGB16	= 9,	//! 48-bit RGB image			: 3 x 16-bit
+	FIT_RGBA16	= 10,	//! 64-bit RGBA image		: 4 x 16-bit
+	FIT_RGBF	= 11,	//! 96-bit RGB float image	: 3 x 32-bit IEEE floating point
+	FIT_RGBAF	= 12	//! 128-bit RGBA float image	: 4 x 32-bit IEEE floating point
+};
+
+/** Image color type used in FreeImage.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_TYPE) {
+	FIC_MINISWHITE = 0,		//! min value is white
+    FIC_MINISBLACK = 1,		//! min value is black
+    FIC_RGB        = 2,		//! RGB color model
+    FIC_PALETTE    = 3,		//! color map indexed
+	FIC_RGBALPHA   = 4,		//! RGB color model with alpha channel
+	FIC_CMYK       = 5		//! CMYK color model
+};
+
+/** Color quantization algorithms.
+Constants used in FreeImage_ColorQuantize.
+*/
+FI_ENUM(FREE_IMAGE_QUANTIZE) {
+    FIQ_WUQUANT = 0,		//! Xiaolin Wu color quantization algorithm
+    FIQ_NNQUANT = 1,		//! NeuQuant neural-net quantization algorithm by Anthony Dekker
+	FIQ_LFPQUANT = 2		//! Lossless Fast Pseudo-Quantization Algorithm by Carsten Klein
+};
+
+/** Dithering algorithms.
+Constants used in FreeImage_Dither.
+*/
+FI_ENUM(FREE_IMAGE_DITHER) {
+    FID_FS			= 0,	//! Floyd & Steinberg error diffusion
+	FID_BAYER4x4	= 1,	//! Bayer ordered dispersed dot dithering (order 2 dithering matrix)
+	FID_BAYER8x8	= 2,	//! Bayer ordered dispersed dot dithering (order 3 dithering matrix)
+	FID_CLUSTER6x6	= 3,	//! Ordered clustered dot dithering (order 3 - 6x6 matrix)
+	FID_CLUSTER8x8	= 4,	//! Ordered clustered dot dithering (order 4 - 8x8 matrix)
+	FID_CLUSTER16x16= 5,	//! Ordered clustered dot dithering (order 8 - 16x16 matrix)
+	FID_BAYER16x16	= 6		//! Bayer ordered dispersed dot dithering (order 4 dithering matrix)
+};
+
+/** Lossless JPEG transformations
+Constants used in FreeImage_JPEGTransform
+*/
+FI_ENUM(FREE_IMAGE_JPEG_OPERATION) {
+	FIJPEG_OP_NONE			= 0,	//! no transformation
+	FIJPEG_OP_FLIP_H		= 1,	//! horizontal flip
+	FIJPEG_OP_FLIP_V		= 2,	//! vertical flip
+	FIJPEG_OP_TRANSPOSE		= 3,	//! transpose across UL-to-LR axis
+	FIJPEG_OP_TRANSVERSE	= 4,	//! transpose across UR-to-LL axis
+	FIJPEG_OP_ROTATE_90		= 5,	//! 90-degree clockwise rotation
+	FIJPEG_OP_ROTATE_180	= 6,	//! 180-degree rotation
+	FIJPEG_OP_ROTATE_270	= 7		//! 270-degree clockwise (or 90 ccw)
+};
+
+/** Tone mapping operators.
+Constants used in FreeImage_ToneMapping.
+*/
+FI_ENUM(FREE_IMAGE_TMO) {
+    FITMO_DRAGO03	 = 0,	//! Adaptive logarithmic mapping (F. Drago, 2003)
+	FITMO_REINHARD05 = 1,	//! Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005)
+	FITMO_FATTAL02	 = 2	//! Gradient domain high dynamic range compression (R. Fattal, 2002)
+};
+
+/** Upsampling / downsampling filters. 
+Constants used in FreeImage_Rescale.
+*/
+FI_ENUM(FREE_IMAGE_FILTER) {
+	FILTER_BOX		  = 0,	//! Box, pulse, Fourier window, 1st order (constant) b-spline
+	FILTER_BICUBIC	  = 1,	//! Mitchell & Netravali's two-param cubic filter
+	FILTER_BILINEAR   = 2,	//! Bilinear filter
+	FILTER_BSPLINE	  = 3,	//! 4th order (cubic) b-spline
+	FILTER_CATMULLROM = 4,	//! Catmull-Rom spline, Overhauser spline
+	FILTER_LANCZOS3	  = 5	//! Lanczos3 filter
+};
+
+/** Color channels.
+Constants used in color manipulation routines.
+*/
+FI_ENUM(FREE_IMAGE_COLOR_CHANNEL) {
+	FICC_RGB	= 0,	//! Use red, green and blue channels
+	FICC_RED	= 1,	//! Use red channel
+	FICC_GREEN	= 2,	//! Use green channel
+	FICC_BLUE	= 3,	//! Use blue channel
+	FICC_ALPHA	= 4,	//! Use alpha channel
+	FICC_BLACK	= 5,	//! Use black channel
+	FICC_REAL	= 6,	//! Complex images: use real part
+	FICC_IMAG	= 7,	//! Complex images: use imaginary part
+	FICC_MAG	= 8,	//! Complex images: use magnitude
+	FICC_PHASE	= 9		//! Complex images: use phase
+};
+
+// Metadata support ---------------------------------------------------------
+
+/**
+  Tag data type information (based on TIFF specifications)
+
+  Note: RATIONALs are the ratio of two 32-bit integer values.
+*/
+FI_ENUM(FREE_IMAGE_MDTYPE) {
+	FIDT_NOTYPE		= 0,	//! placeholder 
+	FIDT_BYTE		= 1,	//! 8-bit unsigned integer 
+	FIDT_ASCII		= 2,	//! 8-bit bytes w/ last byte null 
+	FIDT_SHORT		= 3,	//! 16-bit unsigned integer 
+	FIDT_LONG		= 4,	//! 32-bit unsigned integer 
+	FIDT_RATIONAL	= 5,	//! 64-bit unsigned fraction 
+	FIDT_SBYTE		= 6,	//! 8-bit signed integer 
+	FIDT_UNDEFINED	= 7,	//! 8-bit untyped data 
+	FIDT_SSHORT		= 8,	//! 16-bit signed integer 
+	FIDT_SLONG		= 9,	//! 32-bit signed integer 
+	FIDT_SRATIONAL	= 10,	//! 64-bit signed fraction 
+	FIDT_FLOAT		= 11,	//! 32-bit IEEE floating point 
+	FIDT_DOUBLE		= 12,	//! 64-bit IEEE floating point 
+	FIDT_IFD		= 13,	//! 32-bit unsigned integer (offset) 
+	FIDT_PALETTE	= 14,	//! 32-bit RGBQUAD 
+	FIDT_LONG8		= 16,	//! 64-bit unsigned integer 
+	FIDT_SLONG8		= 17,	//! 64-bit signed integer
+	FIDT_IFD8		= 18	//! 64-bit unsigned integer (offset)
+};
+
+/**
+  Metadata models supported by FreeImage
+*/
+FI_ENUM(FREE_IMAGE_MDMODEL) {
+	FIMD_NODATA			= -1,
+	FIMD_COMMENTS		= 0,	//! single comment or keywords
+	FIMD_EXIF_MAIN		= 1,	//! Exif-TIFF metadata
+	FIMD_EXIF_EXIF		= 2,	//! Exif-specific metadata
+	FIMD_EXIF_GPS		= 3,	//! Exif GPS metadata
+	FIMD_EXIF_MAKERNOTE = 4,	//! Exif maker note metadata
+	FIMD_EXIF_INTEROP	= 5,	//! Exif interoperability metadata
+	FIMD_IPTC			= 6,	//! IPTC/NAA metadata
+	FIMD_XMP			= 7,	//! Abobe XMP metadata
+	FIMD_GEOTIFF		= 8,	//! GeoTIFF metadata
+	FIMD_ANIMATION		= 9,	//! Animation metadata
+	FIMD_CUSTOM			= 10,	//! Used to attach other metadata types to a dib
+	FIMD_EXIF_RAW		= 11	//! Exif metadata as a raw buffer
+};
+
+/**
+  Handle to a metadata model
+*/
+FI_STRUCT (FIMETADATA) { void *data; };
+
+/**
+  Handle to a FreeImage tag
+*/
+FI_STRUCT (FITAG) { void *data; };
+
+// File IO routines ---------------------------------------------------------
+
+#ifndef FREEIMAGE_IO
+#define FREEIMAGE_IO
+
+typedef void* fi_handle;
+typedef unsigned (DLL_CALLCONV *FI_ReadProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef unsigned (DLL_CALLCONV *FI_WriteProc) (void *buffer, unsigned size, unsigned count, fi_handle handle);
+typedef int (DLL_CALLCONV *FI_SeekProc) (fi_handle handle, long offset, int origin);
+typedef long (DLL_CALLCONV *FI_TellProc) (fi_handle handle);
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif // WIN32
+
+FI_STRUCT(FreeImageIO) {
+	FI_ReadProc  read_proc;     //! pointer to the function used to read data
+    FI_WriteProc write_proc;    //! pointer to the function used to write data
+    FI_SeekProc  seek_proc;     //! pointer to the function used to seek
+    FI_TellProc  tell_proc;     //! pointer to the function used to aquire the current position
+};
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif // WIN32
+
+/**
+Handle to a memory I/O stream
+*/
+FI_STRUCT (FIMEMORY) { void *data; };
+
+#endif // FREEIMAGE_IO
+
+// Plugin routines ----------------------------------------------------------
+
+#ifndef PLUGINS
+#define PLUGINS
+
+typedef const char *(DLL_CALLCONV *FI_FormatProc)(void);
+typedef const char *(DLL_CALLCONV *FI_DescriptionProc)(void);
+typedef const char *(DLL_CALLCONV *FI_ExtensionListProc)(void);
+typedef const char *(DLL_CALLCONV *FI_RegExprProc)(void);
+typedef void *(DLL_CALLCONV *FI_OpenProc)(FreeImageIO *io, fi_handle handle, BOOL read);
+typedef void (DLL_CALLCONV *FI_CloseProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCountProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef int (DLL_CALLCONV *FI_PageCapabilityProc)(FreeImageIO *io, fi_handle handle, void *data);
+typedef FIBITMAP *(DLL_CALLCONV *FI_LoadProc)(FreeImageIO *io, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_SaveProc)(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data);
+typedef BOOL (DLL_CALLCONV *FI_ValidateProc)(FreeImageIO *io, fi_handle handle);
+typedef const char *(DLL_CALLCONV *FI_MimeProc)(void);
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportBPPProc)(int bpp);
+typedef BOOL (DLL_CALLCONV *FI_SupportsExportTypeProc)(FREE_IMAGE_TYPE type);
+typedef BOOL (DLL_CALLCONV *FI_SupportsICCProfilesProc)(void);
+typedef BOOL (DLL_CALLCONV *FI_SupportsNoPixelsProc)(void);
+
+FI_STRUCT (Plugin) {
+	FI_FormatProc format_proc;
+	FI_DescriptionProc description_proc;
+	FI_ExtensionListProc extension_proc;
+	FI_RegExprProc regexpr_proc;
+	FI_OpenProc open_proc;
+	FI_CloseProc close_proc;
+	FI_PageCountProc pagecount_proc;
+	FI_PageCapabilityProc pagecapability_proc;
+	FI_LoadProc load_proc;
+	FI_SaveProc save_proc;
+	FI_ValidateProc validate_proc;
+	FI_MimeProc mime_proc;
+	FI_SupportsExportBPPProc supports_export_bpp_proc;
+	FI_SupportsExportTypeProc supports_export_type_proc;
+	FI_SupportsICCProfilesProc supports_icc_profiles_proc;
+	FI_SupportsNoPixelsProc supports_no_pixels_proc;
+};
+
+typedef void (DLL_CALLCONV *FI_InitProc)(Plugin *plugin, int format_id);
+
+#endif // PLUGINS
+
+
+// Load / Save flag constants -----------------------------------------------
+
+#define FIF_LOAD_NOPIXELS 0x8000	//! loading: load the image header only (not supported by all plugins, default to full loading)
+
+#define BMP_DEFAULT         0
+#define BMP_SAVE_RLE        1
+#define CUT_DEFAULT         0
+#define DDS_DEFAULT			0
+#define EXR_DEFAULT			0		//! save data as half with piz-based wavelet compression
+#define EXR_FLOAT			0x0001	//! save data as float instead of as half (not recommended)
+#define EXR_NONE			0x0002	//! save with no compression
+#define EXR_ZIP				0x0004	//! save with zlib compression, in blocks of 16 scan lines
+#define EXR_PIZ				0x0008	//! save with piz-based wavelet compression
+#define EXR_PXR24			0x0010	//! save with lossy 24-bit float compression
+#define EXR_B44				0x0020	//! save with lossy 44% float compression - goes to 22% when combined with EXR_LC
+#define EXR_LC				0x0040	//! save images with one luminance and two chroma channels, rather than as RGB (lossy compression)
+#define FAXG3_DEFAULT		0
+#define GIF_DEFAULT			0
+#define GIF_LOAD256			1		//! load the image as a 256 color image with ununsed palette entries, if it's 16 or 2 color
+#define GIF_PLAYBACK		2		//! 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
+#define HDR_DEFAULT			0
+#define ICO_DEFAULT         0
+#define ICO_MAKEALPHA		1		//! convert to 32bpp and create an alpha channel from the AND-mask when loading
+#define IFF_DEFAULT         0
+#define J2K_DEFAULT			0		//! save with a 16:1 rate
+#define JP2_DEFAULT			0		//! save with a 16:1 rate
+#define JPEG_DEFAULT        0		//! loading (see JPEG_FAST); saving (see JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420)
+#define JPEG_FAST           0x0001	//! load the file as fast as possible, sacrificing some quality
+#define JPEG_ACCURATE       0x0002	//! load the file with the best quality, sacrificing some speed
+#define JPEG_CMYK			0x0004	//! load separated CMYK "as is" (use | to combine with other load flags)
+#define JPEG_EXIFROTATE		0x0008	//! load and rotate according to Exif 'Orientation' tag if available
+#define JPEG_GREYSCALE		0x0010	//! load and convert to a 8-bit greyscale image
+#define JPEG_QUALITYSUPERB  0x80	//! save with superb quality (100:1)
+#define JPEG_QUALITYGOOD    0x0100	//! save with good quality (75:1)
+#define JPEG_QUALITYNORMAL  0x0200	//! save with normal quality (50:1)
+#define JPEG_QUALITYAVERAGE 0x0400	//! save with average quality (25:1)
+#define JPEG_QUALITYBAD     0x0800	//! save with bad quality (10:1)
+#define JPEG_PROGRESSIVE	0x2000	//! save as a progressive-JPEG (use | to combine with other save flags)
+#define JPEG_SUBSAMPLING_411 0x1000		//! save with high 4x1 chroma subsampling (4:1:1) 
+#define JPEG_SUBSAMPLING_420 0x4000		//! save with medium 2x2 medium chroma subsampling (4:2:0) - default value
+#define JPEG_SUBSAMPLING_422 0x8000		//! save with low 2x1 chroma subsampling (4:2:2) 
+#define JPEG_SUBSAMPLING_444 0x10000	//! save with no chroma subsampling (4:4:4)
+#define JPEG_OPTIMIZE		0x20000		//! on saving, compute optimal Huffman coding tables (can reduce a few percent of file size)
+#define JPEG_BASELINE		0x40000		//! save basic JPEG, without metadata or any markers
+#define KOALA_DEFAULT       0
+#define LBM_DEFAULT         0
+#define MNG_DEFAULT         0
+#define PCD_DEFAULT         0
+#define PCD_BASE            1		//! load the bitmap sized 768 x 512
+#define PCD_BASEDIV4        2		//! load the bitmap sized 384 x 256
+#define PCD_BASEDIV16       3		//! load the bitmap sized 192 x 128
+#define PCX_DEFAULT         0
+#define PFM_DEFAULT         0
+#define PICT_DEFAULT        0
+#define PNG_DEFAULT         0
+#define PNG_IGNOREGAMMA		1		//! loading: avoid gamma correction
+#define PNG_Z_BEST_SPEED			0x0001	//! save using ZLib level 1 compression flag (default value is 6)
+#define PNG_Z_DEFAULT_COMPRESSION	0x0006	//! save using ZLib level 6 compression flag (default recommended value)
+#define PNG_Z_BEST_COMPRESSION		0x0009	//! save using ZLib level 9 compression flag (default value is 6)
+#define PNG_Z_NO_COMPRESSION		0x0100	//! save without ZLib compression
+#define PNG_INTERLACED				0x0200	//! save using Adam7 interlacing (use | to combine with other save flags)
+#define PNM_DEFAULT         0
+#define PNM_SAVE_RAW        0       //! if set the writer saves in RAW format (i.e. P4, P5 or P6)
+#define PNM_SAVE_ASCII      1       //! if set the writer saves in ASCII format (i.e. P1, P2 or P3)
+#define PSD_DEFAULT         0
+#define PSD_CMYK			1		//! reads tags for separated CMYK (default is conversion to RGB)
+#define PSD_LAB				2		//! reads tags for CIELab (default is conversion to RGB)
+#define RAS_DEFAULT         0
+#define RAW_DEFAULT         0		//! load the file as linear RGB 48-bit
+#define RAW_PREVIEW			1		//! try to load the embedded JPEG preview with included Exif Data or default to RGB 24-bit
+#define RAW_DISPLAY			2		//! load the file as RGB 24-bit
+#define RAW_HALFSIZE		4		//! output a half-size color image
+#define RAW_UNPROCESSED		8		//! output a FIT_UINT16 raw Bayer image
+#define SGI_DEFAULT			0
+#define TARGA_DEFAULT       0
+#define TARGA_LOAD_RGB888   1       //! if set the loader converts RGB555 and ARGB8888 -> RGB888.
+#define TARGA_SAVE_RLE		2		//! if set, the writer saves with RLE compression
+#define TIFF_DEFAULT        0
+#define TIFF_CMYK			0x0001	//! reads/stores tags for separated CMYK (use | to combine with compression flags)
+#define TIFF_PACKBITS       0x0100  //! save using PACKBITS compression
+#define TIFF_DEFLATE        0x0200  //! save using DEFLATE compression (a.k.a. ZLIB compression)
+#define TIFF_ADOBE_DEFLATE  0x0400  //! save using ADOBE DEFLATE compression
+#define TIFF_NONE           0x0800  //! save without any compression
+#define TIFF_CCITTFAX3		0x1000  //! save using CCITT Group 3 fax encoding
+#define TIFF_CCITTFAX4		0x2000  //! save using CCITT Group 4 fax encoding
+#define TIFF_LZW			0x4000	//! save using LZW compression
+#define TIFF_JPEG			0x8000	//! save using JPEG compression
+#define TIFF_LOGLUV			0x10000	//! save using LogLuv compression
+#define WBMP_DEFAULT        0
+#define XBM_DEFAULT			0
+#define XPM_DEFAULT			0
+#define WEBP_DEFAULT		0		//! save with good quality (75:1)
+#define WEBP_LOSSLESS		0x100	//! save in lossless mode
+#define JXR_DEFAULT			0		//! save with quality 80 and no chroma subsampling (4:4:4)
+#define JXR_LOSSLESS		0x0064	//! save lossless
+#define JXR_PROGRESSIVE		0x2000	//! save as a progressive-JXR (use | to combine with other save flags)
+
+// Background filling options ---------------------------------------------------------
+// Constants used in FreeImage_FillBackground and FreeImage_EnlargeCanvas
+
+#define FI_COLOR_IS_RGB_COLOR			0x00	//! RGBQUAD color is a RGB color (contains no valid alpha channel)
+#define FI_COLOR_IS_RGBA_COLOR			0x01	//! RGBQUAD color is a RGBA color (contains a valid alpha channel)
+#define FI_COLOR_FIND_EQUAL_COLOR		0x02	//! For palettized images: lookup equal RGB color from palette
+#define FI_COLOR_ALPHA_IS_INDEX			0x04	//! The color's rgbReserved member (alpha) contains the palette index to be used
+#define FI_COLOR_PALETTE_SEARCH_MASK	(FI_COLOR_FIND_EQUAL_COLOR | FI_COLOR_ALPHA_IS_INDEX)	// No color lookup is performed
+
+// RescaleEx options ---------------------------------------------------------
+// Constants used in FreeImage_RescaleEx
+
+#define FI_RESCALE_DEFAULT			0x00    //! default options; none of the following other options apply
+#define FI_RESCALE_TRUE_COLOR		0x01	//! for non-transparent greyscale images, convert to 24-bit if src bitdepth <= 8 (default is a 8-bit greyscale image). 
+#define FI_RESCALE_OMIT_METADATA	0x02	//! do not copy metadata to the rescaled image
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Init / Error routines ----------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_Initialise(BOOL load_local_plugins_only FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_DeInitialise(void);
+
+// Version routines ---------------------------------------------------------
+
+DLL_API const char *DLL_CALLCONV FreeImage_GetVersion(void);
+DLL_API const char *DLL_CALLCONV FreeImage_GetCopyrightMessage(void);
+
+// Message output functions -------------------------------------------------
+
+typedef void (*FreeImage_OutputMessageFunction)(FREE_IMAGE_FORMAT fif, const char *msg);
+typedef void (DLL_CALLCONV *FreeImage_OutputMessageFunctionStdCall)(FREE_IMAGE_FORMAT fif, const char *msg); 
+
+DLL_API void DLL_CALLCONV FreeImage_SetOutputMessageStdCall(FreeImage_OutputMessageFunctionStdCall omf); 
+DLL_API void DLL_CALLCONV FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf);
+DLL_API void DLL_CALLCONV FreeImage_OutputMessageProc(int fif, const char *fmt, ...);
+
+// Allocate / Clone / Unload routines ---------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_Unload(FIBITMAP *dib);
+
+// Header loading routines
+DLL_API BOOL DLL_CALLCONV FreeImage_HasPixels(FIBITMAP *dib);
+
+// Load / Save routines -----------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Load(FREE_IMAGE_FORMAT fif, const char *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_Save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveU(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToHandle(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+
+// Memory I/O stream routines -----------------------------------------------
+
+DLL_API FIMEMORY *DLL_CALLCONV FreeImage_OpenMemory(BYTE *data FI_DEFAULT(0), DWORD size_in_bytes FI_DEFAULT(0));
+DLL_API void DLL_CALLCONV FreeImage_CloseMemory(FIMEMORY *stream);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_LoadFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveToMemory(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API long DLL_CALLCONV FreeImage_TellMemory(FIMEMORY *stream);
+DLL_API BOOL DLL_CALLCONV FreeImage_SeekMemory(FIMEMORY *stream, long offset, int origin);
+DLL_API BOOL DLL_CALLCONV FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes);
+DLL_API unsigned DLL_CALLCONV FreeImage_ReadMemory(void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+DLL_API unsigned DLL_CALLCONV FreeImage_WriteMemory(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream);
+
+DLL_API FIMULTIBITMAP *DLL_CALLCONV FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags);
+
+// Plugin Interface ---------------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterLocalPlugin(FI_InitProc proc_address, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_RegisterExternalPlugin(const char *path, const char *format FI_DEFAULT(0), const char *description FI_DEFAULT(0), const char *extension FI_DEFAULT(0), const char *regexpr FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetFIFCount(void);
+DLL_API int DLL_CALLCONV FreeImage_SetPluginEnabled(FREE_IMAGE_FORMAT fif, BOOL enable);
+DLL_API int DLL_CALLCONV FreeImage_IsPluginEnabled(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFormat(const char *format);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromMime(const char *mime);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFormatFromFIF(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFExtensionList(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFDescription(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFRegExpr(FREE_IMAGE_FORMAT fif);
+DLL_API const char *DLL_CALLCONV FreeImage_GetFIFMimeType(FREE_IMAGE_FORMAT fif);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilename(const char *filename);
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFIFFromFilenameU(const wchar_t *filename);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsReading(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsWriting(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportBPP(FREE_IMAGE_FORMAT fif, int bpp);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsExportType(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsICCProfiles(FREE_IMAGE_FORMAT fif);
+DLL_API BOOL DLL_CALLCONV FreeImage_FIFSupportsNoPixels(FREE_IMAGE_FORMAT fif);
+
+// Multipaging interface ----------------------------------------------------
+
+DLL_API FIMULTIBITMAP * DLL_CALLCONV FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory FI_DEFAULT(FALSE), int flags FI_DEFAULT(0));
+DLL_API FIMULTIBITMAP * DLL_CALLCONV FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags FI_DEFAULT(0));
+DLL_API BOOL DLL_CALLCONV FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags FI_DEFAULT(0));
+DLL_API int DLL_CALLCONV FreeImage_GetPageCount(FIMULTIBITMAP *bitmap);
+DLL_API void DLL_CALLCONV FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data);
+DLL_API void DLL_CALLCONV FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page);
+DLL_API FIBITMAP * DLL_CALLCONV FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page);
+DLL_API void DLL_CALLCONV FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *data, BOOL changed);
+DLL_API BOOL DLL_CALLCONV FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count);
+
+// Filetype request routines ------------------------------------------------
+
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileType(const char *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeU(const wchar_t *filename, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromHandle(FreeImageIO *io, fi_handle handle, int size FI_DEFAULT(0));
+DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetFileTypeFromMemory(FIMEMORY *stream, int size FI_DEFAULT(0));
+
+// Image type request routine -----------------------------------------------
+
+DLL_API FREE_IMAGE_TYPE DLL_CALLCONV FreeImage_GetImageType(FIBITMAP *dib);
+
+// FreeImage helper routines ------------------------------------------------
+
+DLL_API BOOL DLL_CALLCONV FreeImage_IsLittleEndian(void);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+DLL_API BOOL DLL_CALLCONV FreeImage_LookupSVGColor(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue);
+
+// Pixel access routines ----------------------------------------------------
+
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetBits(FIBITMAP *dib);
+DLL_API BYTE *DLL_CALLCONV FreeImage_GetScanLine(FIBITMAP *dib, int scanline);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value);
+
+// DIB info routines --------------------------------------------------------
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetColorsUsed(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBPP(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetWidth(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetHeight(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetLine(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetPitch(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDIBSize(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetMemorySize(FIBITMAP *dib);
+DLL_API RGBQUAD *DLL_CALLCONV FreeImage_GetPalette(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterX(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterY(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res);
+DLL_API void DLL_CALLCONV FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res);
+
+DLL_API BITMAPINFOHEADER *DLL_CALLCONV FreeImage_GetInfoHeader(FIBITMAP *dib);
+DLL_API BITMAPINFO *DLL_CALLCONV FreeImage_GetInfo(FIBITMAP *dib);
+DLL_API FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetRedMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetGreenMask(FIBITMAP *dib);
+DLL_API unsigned DLL_CALLCONV FreeImage_GetBlueMask(FIBITMAP *dib);
+
+DLL_API unsigned DLL_CALLCONV FreeImage_GetTransparencyCount(FIBITMAP *dib);
+DLL_API BYTE * DLL_CALLCONV FreeImage_GetTransparencyTable(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count);
+DLL_API BOOL DLL_CALLCONV FreeImage_IsTransparent(FIBITMAP *dib);
+DLL_API void DLL_CALLCONV FreeImage_SetTransparentIndex(FIBITMAP *dib, int index);
+DLL_API int DLL_CALLCONV FreeImage_GetTransparentIndex(FIBITMAP *dib);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_HasBackgroundColor(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetThumbnail(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail);
+
+// ICC profile routines -----------------------------------------------------
+
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_GetICCProfile(FIBITMAP *dib);
+DLL_API FIICCPROFILE *DLL_CALLCONV FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size);
+DLL_API void DLL_CALLCONV FreeImage_DestroyICCProfile(FIBITMAP *dib);
+
+// Line conversion routines -------------------------------------------------
+
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To4(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To4_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To4(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To8_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To8(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_565_To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16_555_To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To16_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels);
+DLL_API void DLL_CALLCONV FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels);
+
+// Smart conversion routines ------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo4Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo8Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToGreyscale(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits555(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo16Bits565(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo24Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertTo32Bits(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize FI_DEFAULT(FIQ_WUQUANT), int PaletteSize FI_DEFAULT(256), int ReserveSize FI_DEFAULT(0), RGBQUAD *ReservePalette FI_DEFAULT(NULL));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Threshold(FIBITMAP *dib, BYTE T);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+DLL_API void DLL_CALLCONV FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE));
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToFloat(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBF(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBAF(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToUINT16(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGB16(FIBITMAP *dib);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBA16(FIBITMAP *dib);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE));
+
+// Tone mapping operators ---------------------------------------------------
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ToneMapping(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param FI_DEFAULT(0), double second_param FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_TmoDrago03(FIBITMAP *src, double gamma FI_DEFAULT(2.2), double exposure FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_TmoReinhard05(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_TmoReinhard05Ex(FIBITMAP *src, double intensity FI_DEFAULT(0), double contrast FI_DEFAULT(0), double adaptation FI_DEFAULT(1), double color_correction FI_DEFAULT(0));
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_TmoFattal02(FIBITMAP *src, double color_saturation FI_DEFAULT(0.5), double attenuation FI_DEFAULT(0.85));
+
+// ZLib interface -----------------------------------------------------------
+
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size);
+DLL_API DWORD DLL_CALLCONV FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size);
+
+// --------------------------------------------------------------------------
+// Metadata routines
+// --------------------------------------------------------------------------
+
+// tag creation / destruction
+DLL_API FITAG *DLL_CALLCONV FreeImage_CreateTag(void);
+DLL_API void DLL_CALLCONV FreeImage_DeleteTag(FITAG *tag);
+DLL_API FITAG *DLL_CALLCONV FreeImage_CloneTag(FITAG *tag);
+
+// tag getters and setters
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagKey(FITAG *tag);
+DLL_API const char *DLL_CALLCONV FreeImage_GetTagDescription(FITAG *tag);
+DLL_API WORD DLL_CALLCONV FreeImage_GetTagID(FITAG *tag);
+DLL_API FREE_IMAGE_MDTYPE DLL_CALLCONV FreeImage_GetTagType(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagCount(FITAG *tag);
+DLL_API DWORD DLL_CALLCONV FreeImage_GetTagLength(FITAG *tag);
+DLL_API const void *DLL_CALLCONV FreeImage_GetTagValue(FITAG *tag);
+
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagKey(FITAG *tag, const char *key);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagDescription(FITAG *tag, const char *description);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagID(FITAG *tag, WORD id);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagType(FITAG *tag, FREE_IMAGE_MDTYPE type);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagCount(FITAG *tag, DWORD count);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagLength(FITAG *tag, DWORD length);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetTagValue(FITAG *tag, const void *value);
+
+// iterator
+DLL_API FIMETADATA *DLL_CALLCONV FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag);
+DLL_API void DLL_CALLCONV FreeImage_FindCloseMetadata(FIMETADATA *mdhandle);
+
+// metadata setter and getter
+DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadataKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value);
+
+// helpers
+DLL_API unsigned DLL_CALLCONV FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src);
+
+// tag to C string conversion
+DLL_API const char* DLL_CALLCONV FreeImage_TagToString(FREE_IMAGE_MDMODEL model, FITAG *tag, char *Make FI_DEFAULT(NULL));
+
+// --------------------------------------------------------------------------
+// JPEG lossless transformation routines
+// --------------------------------------------------------------------------
+
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(TRUE));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(TRUE));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom);
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom);
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
+
+
+// --------------------------------------------------------------------------
+// Image manipulation toolkit
+// --------------------------------------------------------------------------
+
+// rotation and flipping
+/// @deprecated see FreeImage_Rotate
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateClassic(FIBITMAP *dib, double angle);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor FI_DEFAULT(NULL));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipHorizontal(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_FlipVertical(FIBITMAP *dib);
+
+// upsampling / downsampling
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Rescale(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter FI_DEFAULT(FILTER_CATMULLROM));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RescaleRect(FIBITMAP *dib, int dst_width, int dst_height, int left, int top, int right, int bottom, FREE_IMAGE_FILTER filter FI_DEFAULT(FILTER_CATMULLROM), unsigned flags FI_DEFAULT(0));
+
+// color manipulation routines (point operations)
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustCurve(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustGamma(FIBITMAP *dib, double gamma);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustBrightness(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustContrast(FIBITMAP *dib, double percentage);
+DLL_API BOOL DLL_CALLCONV FreeImage_Invert(FIBITMAP *dib);
+DLL_API BOOL DLL_CALLCONV FreeImage_GetHistogram(FIBITMAP *dib, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel FI_DEFAULT(FICC_BLACK));
+DLL_API int DLL_CALLCONV FreeImage_GetAdjustColorsLookupTable(BYTE *LUT, double brightness, double contrast, double gamma, BOOL invert);
+DLL_API BOOL DLL_CALLCONV FreeImage_AdjustColors(FIBITMAP *dib, double brightness, double contrast, double gamma, BOOL invert FI_DEFAULT(FALSE));
+DLL_API unsigned DLL_CALLCONV FreeImage_ApplyColorMapping(FIBITMAP *dib, RGBQUAD *srccolors, RGBQUAD *dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap);
+DLL_API unsigned DLL_CALLCONV FreeImage_SwapColors(FIBITMAP *dib, RGBQUAD *color_a, RGBQUAD *color_b, BOOL ignore_alpha);
+DLL_API unsigned DLL_CALLCONV FreeImage_ApplyPaletteIndexMapping(FIBITMAP *dib, BYTE *srcindices,	BYTE *dstindices, unsigned count, BOOL swap);
+DLL_API unsigned DLL_CALLCONV FreeImage_SwapPaletteIndices(FIBITMAP *dib, BYTE *index_a, BYTE *index_b);
+
+// channel processing routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetChannel(FIBITMAP *dib, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+DLL_API BOOL DLL_CALLCONV FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel);
+
+// copy / paste / composite routines
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Copy(FIBITMAP *dib, int left, int top, int right, int bottom);
+DLL_API BOOL DLL_CALLCONV FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha);
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom);
+
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL));
+DLL_API BOOL DLL_CALLCONV FreeImage_PreMultiplyWithAlpha(FIBITMAP *dib);
+
+// background filling routines
+DLL_API BOOL DLL_CALLCONV FreeImage_FillBackground(FIBITMAP *dib, const void *color, int options FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_EnlargeCanvas(FIBITMAP *src, int left, int top, int right, int bottom, const void *color, int options FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_AllocateEx(int width, int height, int bpp, const RGBQUAD *color, int options FI_DEFAULT(0), const RGBQUAD *palette FI_DEFAULT(NULL), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_AllocateExT(FREE_IMAGE_TYPE type, int width, int height, int bpp, const void *color, int options FI_DEFAULT(0), const RGBQUAD *palette FI_DEFAULT(NULL), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));
+
+// miscellaneous algorithms
+DLL_API FIBITMAP *DLL_CALLCONV FreeImage_MultigridPoissonSolver(FIBITMAP *Laplacian, int ncycle FI_DEFAULT(3));
+
+// restore the borland-specific enum size option
+#if defined(__BORLANDC__)
+#pragma option pop
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // FREEIMAGE_H
diff --git a/files/Source/FreeImage/BitmapAccess.cpp b/files/Source/FreeImage/BitmapAccess.cpp
new file mode 100644
index 0000000..347ad1f
--- /dev/null
+++ b/files/Source/FreeImage/BitmapAccess.cpp
@@ -0,0 +1,1573 @@
+// ==========================================================
+// FreeImage implementation
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Petr Supina (psup@centrum.cz)
+// - Carsten Klein (c.klein@datagis.com)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif 
+
+#include <stdlib.h>
+#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__)
+#include <malloc.h>
+#endif // _WIN32 || _WIN64 || __MINGW32__
+
+#include "FreeImage.h"
+#include "FreeImageIO.h"
+#include "Utilities.h"
+#include "MapIntrospector.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+/**
+Constants for the BITMAPINFOHEADER::biCompression field
+BI_RGB:
+The bitmap is in uncompressed red green blue (RGB) format that is not compressed and does not use color masks.
+BI_BITFIELDS:
+The bitmap is not compressed and the color table consists of three DWORD color masks that specify the red, green, and blue components, 
+respectively, of each pixel. This is valid when used with 16 and 32-bits per pixel bitmaps.
+*/
+#ifndef _WINGDI_
+#define BI_RGB       0L
+#define BI_BITFIELDS 3L
+#endif // _WINGDI_
+
+// ----------------------------------------------------------
+//  Metadata definitions
+// ----------------------------------------------------------
+
+/** helper for map<key, value> where value is a pointer to a FreeImage tag */
+typedef std::map<std::string, FITAG*> TAGMAP;
+
+/** helper for map<FREE_IMAGE_MDMODEL, TAGMAP*> */
+typedef std::map<int, TAGMAP*> METADATAMAP;
+
+/** helper for metadata iterator */
+FI_STRUCT (METADATAHEADER) { 
+	long pos;		//! current position when iterating the map
+	TAGMAP *tagmap;	//! pointer to the tag map
+};
+
+// ----------------------------------------------------------
+//  FIBITMAP definition
+// ----------------------------------------------------------
+
+/**
+FreeImage header structure
+*/
+FI_STRUCT (FREEIMAGEHEADER) {
+	/** data type - bitmap, array of long, double, complex, etc */
+	FREE_IMAGE_TYPE type;
+
+	/** background color used for RGB transparency */
+	RGBQUAD bkgnd_color;
+
+	/**@name transparency management */
+	//@{
+	/**
+	why another table ? for easy transparency table retrieval !
+	transparency could be stored in the palette, which is better
+	overall, but it requires quite some changes and it will render
+	FreeImage_GetTransparencyTable obsolete in its current form;
+	*/
+	BYTE transparent_table[256];
+	/** number of transparent colors */
+	int  transparency_count;
+	/** TRUE if the image is transparent */
+	BOOL transparent;
+	//@}
+
+	/** space to hold ICC profile */
+	FIICCPROFILE iccProfile;
+
+	/** contains a list of metadata models attached to the bitmap */
+	METADATAMAP *metadata;
+
+	/** FALSE if the FIBITMAP only contains the header and no pixel data */
+	BOOL has_pixels;
+
+	/** optionally contains a thumbnail attached to the bitmap */
+	FIBITMAP *thumbnail;
+
+	/**@name external pixel buffer management */
+	//@{
+	/** pointer to user provided pixels, NULL otherwise */
+	BYTE *external_bits;
+	/** user provided pitch, 0 otherwise */
+	unsigned external_pitch;
+	//@}
+
+	//BYTE filler[1];			 // fill to 32-bit alignment
+};
+
+// ----------------------------------------------------------
+//  FREEIMAGERGBMASKS definition
+// ----------------------------------------------------------
+
+/**
+RGB mask structure - mainly used for 16-bit RGB555 / RGB 565 FIBITMAP
+*/
+FI_STRUCT (FREEIMAGERGBMASKS) {
+	unsigned red_mask;		//! bit layout of the red components
+	unsigned green_mask;	//! bit layout of the green components
+	unsigned blue_mask;		//! bit layout of the blue components
+};
+
+// ----------------------------------------------------------
+//  Memory allocation on a specified alignment boundary
+// ----------------------------------------------------------
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+	assert(alignment == FIBITMAP_ALIGNMENT);
+	return _aligned_malloc(amount, alignment);
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+	_aligned_free(mem);
+}
+
+#elif defined (__MINGW32__)
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+	assert(alignment == FIBITMAP_ALIGNMENT);
+	return __mingw_aligned_malloc (amount, alignment);
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+	__mingw_aligned_free (mem);
+}
+
+#else
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+	assert(alignment == FIBITMAP_ALIGNMENT);
+	/*
+	In some rare situations, the malloc routines can return misaligned memory. 
+	The routine FreeImage_Aligned_Malloc allocates a bit more memory to do
+	aligned writes.  Normally, it *should* allocate "alignment" extra memory and then writes
+	one dword back the true pointer.  But if the memory manager returns a
+	misaligned block that is less than a dword from the next alignment, 
+	then the writing back one dword will corrupt memory.
+
+	For example, suppose that alignment is 16 and malloc returns the address 0xFFFF.
+
+	16 - 0xFFFF % 16 + 0xFFFF = 16 - 15 + 0xFFFF = 0x10000.
+
+	Now, you subtract one dword from that and write and that will corrupt memory.
+
+	That's why the code below allocates *two* alignments instead of one. 
+	*/
+	void* mem_real = malloc(amount + 2 * alignment);
+	if(!mem_real) return NULL;
+	char* mem_align = (char*)((unsigned long)(2 * alignment - (unsigned long)mem_real % (unsigned long)alignment) + (unsigned long)mem_real);
+	*((long*)mem_align - 1) = (long)mem_real;
+	return mem_align;
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+	free((void*)*((long*)mem - 1));
+}
+
+#endif // _WIN32 || _WIN64
+
+// ----------------------------------------------------------
+//  FIBITMAP memory management
+// ----------------------------------------------------------
+
+/**
+Calculate the size of a FreeImage image. 
+Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary.
+This function includes a protection against malicious images, based on a KISS integer overflow detection mechanism. 
+
+@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size
+@param width Image width
+@param height Image height
+@param bpp Number of bits-per-pixel
+@param need_masks We only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP
+@return Returns a size in BYTE units
+@see FreeImage_AllocateBitmap
+*/
+static size_t 
+FreeImage_GetInternalImageSize(BOOL header_only, unsigned width, unsigned height, unsigned bpp, BOOL need_masks) {
+	size_t dib_size = sizeof(FREEIMAGEHEADER);
+	dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);
+	dib_size += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT;
+	dib_size += sizeof(BITMAPINFOHEADER);
+	// palette is aligned on a 16 bytes boundary
+	dib_size += sizeof(RGBQUAD) * CalculateUsedPaletteEntries(bpp);
+	// we both add palette size and masks size if need_masks is true, since CalculateUsedPaletteEntries
+	// always returns 0 if need_masks is true (which is only true for 16 bit images).
+	dib_size += need_masks ? sizeof(DWORD) * 3 : 0;
+	dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);
+
+	if(!header_only) {
+		const size_t header_size = dib_size;
+
+		// pixels are aligned on a 16 bytes boundary
+		dib_size += (size_t)CalculatePitch(CalculateLine(width, bpp)) * (size_t)height;
+
+		// check for possible malloc overflow using a KISS integer overflow detection mechanism
+		{
+			const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0;
+			const double dImageSize = (double)header_size + dPitch * height;
+			if(dImageSize != (double)dib_size) {
+				// here, we are sure to encounter a malloc overflow: try to avoid it ...
+				return 0;
+			}
+
+			/*
+			The following constant take into account the additionnal memory used by 
+			aligned malloc functions as well as debug malloc functions. 
+			It is supposed here that using a (8 * FIBITMAP_ALIGNMENT) risk margin will be enough
+			for the target compiler. 
+			*/
+			const double FIBITMAP_MAX_MEMORY = (double)((size_t)-1) - 8 * FIBITMAP_ALIGNMENT;
+
+			if(dImageSize > FIBITMAP_MAX_MEMORY) {
+				// avoid possible overflow inside C allocation functions
+				return 0;
+			}
+		}
+	}
+
+	return dib_size;
+}
+
+/**
+Helper for 16-bit FIT_BITMAP
+Returns a pointer to the bitmap's red-, green- and blue masks.
+@param dib The bitmap to obtain masks from.
+@return Returns a pointer to the bitmap's red-, green- and blue masks
+or NULL, if no masks are present (e.g. for 24 bit images).
+*/
+static FREEIMAGERGBMASKS *
+FreeImage_GetRGBMasks(FIBITMAP *dib) {
+	return FreeImage_HasRGBMasks(dib) ? (FREEIMAGERGBMASKS *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL;
+}
+
+/**
+Internal FIBITMAP allocation.
+
+This function accepts (ext_bits, ext_pitch) arguments. If these are provided the FIBITMAP 
+will be allocated as "header only", but bits and pitch will be stored within the FREEIMAGEHEADER 
+and the resulting FIBITMAP will have pixels, i.e. HasPixels() will return TRUE.
+- GetBits() and GetPitch return the correct values - either offsets or the stored values (user-provided bits and pitch).
+- Clone() creates a new FIBITMAP with copy of the user pixel data.
+- Unload's implementation does not need to change - it just release a "header only" dib.
+Note that when using external data, the data does not need to have the same alignment as the default 4-byte alignment. 
+This enables the possibility to access buffers with, for instance, stricter alignment,
+like the ones used in low-level APIs like OpenCL or intrinsics.
+
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@param ext_bits Pointer to external user's pixel buffer if using wrapped buffer, NULL otherwise
+@param ext_pitch Pointer to external user's pixel buffer pitch if using wrapped buffer, 0 otherwise
+@param type Image type
+@param width Image width
+@param height Image height
+@param bpp Number of bits per pixel
+@param red_mask Image red mask 
+@param green_mask Image green mask
+@param blue_mask Image blue mask
+@return Returns the allocated FIBITMAP if successful, returns NULL otherwise
+*/
+static FIBITMAP * 
+FreeImage_AllocateBitmap(BOOL header_only, BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+
+	// check input variables
+	width = abs(width);
+	height = abs(height);
+	if(!((width > 0) && (height > 0))) {
+		return NULL;
+	}
+	if(ext_bits) {
+		if(ext_pitch == 0) {
+			return NULL;
+		}
+		assert(header_only == FALSE);
+	}
+
+	// we only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP
+	BOOL need_masks = FALSE;
+
+	// check pixel bit depth
+	switch(type) {
+		case FIT_BITMAP:
+			switch(bpp) {
+				case 1:
+				case 4:
+				case 8:
+					break;
+				case 16:
+					need_masks = TRUE;
+					break;
+				case 24:
+				case 32:
+					break;
+				default:
+					bpp = 8;
+					break;
+			}
+			break;
+		case FIT_UINT16:
+			bpp = 8 * sizeof(unsigned short);
+			break;
+		case FIT_INT16:
+			bpp = 8 * sizeof(short);
+			break;
+		case FIT_UINT32:
+			bpp = 8 * sizeof(DWORD);
+			break;
+		case FIT_INT32:
+			bpp = 8 * sizeof(LONG);
+			break;
+		case FIT_FLOAT:
+			bpp = 8 * sizeof(float);
+			break;
+		case FIT_DOUBLE:
+			bpp = 8 * sizeof(double);
+			break;
+		case FIT_COMPLEX:
+			bpp = 8 * sizeof(FICOMPLEX);
+			break;
+		case FIT_RGB16:
+			bpp = 8 * sizeof(FIRGB16);
+			break;
+		case FIT_RGBA16:
+			bpp = 8 * sizeof(FIRGBA16);
+			break;
+		case FIT_RGBF:
+			bpp = 8 * sizeof(FIRGBF);
+			break;
+		case FIT_RGBAF:
+			bpp = 8 * sizeof(FIRGBAF);
+			break;
+		default:
+			return NULL;
+	}
+
+	FIBITMAP *bitmap = (FIBITMAP *)malloc(sizeof(FIBITMAP));
+
+	if (bitmap != NULL) {
+
+		// calculate the size of a FreeImage image
+		// align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
+		// palette is aligned on a 16 bytes boundary
+		// pixels are aligned on a 16 bytes boundary
+
+		// when using a user provided pixel buffer, force a 'header only' allocation
+
+		size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks);
+
+		if(dib_size == 0) {
+			// memory allocation will fail (probably a malloc overflow)
+			free(bitmap);
+			return NULL;
+		}
+
+		bitmap->data = (BYTE *)FreeImage_Aligned_Malloc(dib_size * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+
+		if (bitmap->data != NULL) {
+			memset(bitmap->data, 0, dib_size);
+
+			// write out the FREEIMAGEHEADER
+
+			FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)bitmap->data;
+
+			fih->type = type;
+
+			memset(&fih->bkgnd_color, 0, sizeof(RGBQUAD));
+
+			fih->transparent = FALSE;
+			fih->transparency_count = 0;
+			memset(fih->transparent_table, 0xff, 256);
+
+			fih->has_pixels = header_only ? FALSE : TRUE;
+
+			// initialize FIICCPROFILE link
+
+			FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(bitmap);
+			iccProfile->size = 0;
+			iccProfile->data = 0;
+			iccProfile->flags = 0;
+
+			// initialize metadata models list
+
+			fih->metadata = new(std::nothrow) METADATAMAP;
+
+			// initialize attached thumbnail
+
+			fih->thumbnail = NULL;
+
+			// store a pointer to user provided pixel buffer (if any)
+
+			fih->external_bits = ext_bits;
+			fih->external_pitch = ext_pitch;
+
+			// write out the BITMAPINFOHEADER
+
+			BITMAPINFOHEADER *bih   = FreeImage_GetInfoHeader(bitmap);
+			bih->biSize             = sizeof(BITMAPINFOHEADER);
+			bih->biWidth            = width;
+			bih->biHeight           = height;
+			bih->biPlanes           = 1;
+			bih->biCompression      = need_masks ? BI_BITFIELDS : BI_RGB;
+			bih->biBitCount         = (WORD)bpp;
+			bih->biClrUsed          = CalculateUsedPaletteEntries(bpp);
+			bih->biClrImportant     = bih->biClrUsed;
+			bih->biXPelsPerMeter	= 2835;	// 72 dpi
+			bih->biYPelsPerMeter	= 2835;	// 72 dpi
+
+			if(bpp == 8) {
+				// build a default greyscale palette (very useful for image processing)
+				RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+				for(int i = 0; i < 256; i++) {
+					pal[i].rgbRed	= (BYTE)i;
+					pal[i].rgbGreen = (BYTE)i;
+					pal[i].rgbBlue	= (BYTE)i;
+				}
+			}
+
+			// just setting the masks (only if needed) just like the palette.
+			if (need_masks) {
+				FREEIMAGERGBMASKS *masks = FreeImage_GetRGBMasks(bitmap);
+				masks->red_mask = red_mask;
+				masks->green_mask = green_mask;
+				masks->blue_mask = blue_mask;
+			}
+
+			return bitmap;
+		}
+
+		free(bitmap);
+	}
+
+	return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeaderForBits(BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateBitmap(FALSE, ext_bits, ext_pitch, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateBitmap(header_only, NULL, 0, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateBitmap(header_only, NULL, 0, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateBitmap(FALSE, NULL, 0, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+	return FreeImage_AllocateBitmap(FALSE, NULL, 0, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+void DLL_CALLCONV
+FreeImage_Unload(FIBITMAP *dib) {
+	if (NULL != dib) {	
+		if (NULL != dib->data) {
+			// delete possible icc profile ...
+			if (FreeImage_GetICCProfile(dib)->data) {
+				free(FreeImage_GetICCProfile(dib)->data);
+			}
+
+			// delete metadata models
+			METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+
+			for(METADATAMAP::iterator i = (*metadata).begin(); i != (*metadata).end(); i++) {
+				TAGMAP *tagmap = (*i).second;
+
+				if(tagmap) {
+					for(TAGMAP::iterator j = tagmap->begin(); j != tagmap->end(); j++) {
+						FITAG *tag = (*j).second;
+						FreeImage_DeleteTag(tag);
+					}
+
+					delete tagmap;
+				}
+			}
+
+			delete metadata;
+
+			// delete embedded thumbnail
+			FreeImage_Unload(FreeImage_GetThumbnail(dib));
+
+			// delete bitmap ...
+			FreeImage_Aligned_Free(dib->data);
+		}
+
+		free(dib);		// ... and the wrapper
+	}
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Clone(FIBITMAP *dib) {
+	if(!dib) {
+		return NULL;
+	}
+
+	FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib);
+	unsigned width	= FreeImage_GetWidth(dib);
+	unsigned height	= FreeImage_GetHeight(dib);
+	unsigned bpp	= FreeImage_GetBPP(dib);
+
+	const BYTE *ext_bits = ((FREEIMAGEHEADER *)dib->data)->external_bits;
+	
+	// check for pixel availability ...
+	BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE;
+
+	// check whether this image has masks defined ...
+	BOOL need_masks = (bpp == 16 && type == FIT_BITMAP) ? TRUE : FALSE;
+
+	// allocate a new dib
+	FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, type, width, height, bpp,
+			FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
+
+	if (new_dib) {
+		// save ICC profile links
+		FIICCPROFILE *src_iccProfile = FreeImage_GetICCProfile(dib);
+		FIICCPROFILE *dst_iccProfile = FreeImage_GetICCProfile(new_dib);
+
+		// save metadata links
+		METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+		METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata;
+
+		// calculate the size of the src image
+		// align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
+		// palette is aligned on a 16 bytes boundary
+		// pixels are aligned on a 16 bytes boundary
+		
+		// when using a user provided pixel buffer, force a 'header only' calculation		
+
+		size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks);
+
+		// copy the bitmap + internal pointers (remember to restore new_dib internal pointers later)
+		memcpy(new_dib->data, dib->data, dib_size);
+
+		// reset ICC profile link for new_dib
+		memset(dst_iccProfile, 0, sizeof(FIICCPROFILE));
+
+		// restore metadata link for new_dib
+		((FREEIMAGEHEADER *)new_dib->data)->metadata = dst_metadata;
+
+		// reset thumbnail link for new_dib
+		((FREEIMAGEHEADER *)new_dib->data)->thumbnail = NULL;
+
+		// copy possible ICC profile
+		FreeImage_CreateICCProfile(new_dib, src_iccProfile->data, src_iccProfile->size);
+		dst_iccProfile->flags = src_iccProfile->flags;
+
+		// copy metadata models
+		for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
+			int model = (*i).first;
+			TAGMAP *src_tagmap = (*i).second;
+
+			if(src_tagmap) {
+				// create a metadata model
+				TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
+
+				if(dst_tagmap) {
+					// fill the model
+					for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
+						std::string dst_key = (*j).first;
+						FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
+
+						// assign key and tag value
+						(*dst_tagmap)[dst_key] = dst_tag;
+					}
+
+					// assign model and tagmap
+					(*dst_metadata)[model] = dst_tagmap;
+				}
+			}
+		}
+
+		// copy the thumbnail
+		FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib));
+
+		// copy user provided pixel buffer (if any)
+		if(ext_bits) {
+			const unsigned pitch = FreeImage_GetPitch(dib);
+			const unsigned linesize = FreeImage_GetLine(dib);
+			for(unsigned y = 0; y < height; y++) {
+				memcpy(FreeImage_GetScanLine(new_dib, y), ext_bits, linesize);
+				ext_bits += pitch;
+			}
+		}
+
+		return new_dib;
+	}
+
+	return NULL;
+}
+
+// ----------------------------------------------------------
+
+BYTE * DLL_CALLCONV
+FreeImage_GetBits(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib)) {
+		return NULL;
+	}
+
+	if(((FREEIMAGEHEADER *)dib->data)->external_bits) {
+		return ((FREEIMAGEHEADER *)dib->data)->external_bits;
+	}
+
+	// returns the pixels aligned on a FIBITMAP_ALIGNMENT bytes alignment boundary
+	size_t lp = (size_t)FreeImage_GetInfoHeader(dib);
+	lp += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * FreeImage_GetColorsUsed(dib);
+	lp += FreeImage_HasRGBMasks(dib) ? sizeof(DWORD) * 3 : 0;
+	lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
+	return (BYTE *)lp;
+}
+
+// ----------------------------------------------------------
+//  DIB information functions
+// ----------------------------------------------------------
+
+FIBITMAP* DLL_CALLCONV
+FreeImage_GetThumbnail(FIBITMAP *dib) {
+	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail) {
+	if(dib == NULL) {
+		return FALSE;
+	}
+	FIBITMAP *currentThumbnail = ((FREEIMAGEHEADER *)dib->data)->thumbnail;
+	if(currentThumbnail == thumbnail) {
+		return TRUE;
+	}
+	FreeImage_Unload(currentThumbnail);
+
+	((FREEIMAGEHEADER *)dib->data)->thumbnail = FreeImage_HasPixels(thumbnail) ? FreeImage_Clone(thumbnail) : NULL;
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_COLOR_TYPE DLL_CALLCONV
+FreeImage_GetColorType(FIBITMAP *dib) {
+	RGBQUAD *rgb;
+
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+	// special bitmap type
+	if(image_type != FIT_BITMAP) {
+		switch(image_type) {
+			case FIT_UINT16:
+			{
+				// 16-bit greyscale TIF can be either FIC_MINISBLACK (the most common case) or FIC_MINISWHITE
+				// you can check this using EXIF_MAIN metadata
+				FITAG *photometricTag = NULL;
+				if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "PhotometricInterpretation", &photometricTag)) {
+					const short *value = (short*)FreeImage_GetTagValue(photometricTag);
+					// PHOTOMETRIC_MINISWHITE = 0 => min value is white
+					// PHOTOMETRIC_MINISBLACK = 1 => min value is black
+					return (*value == 0) ? FIC_MINISWHITE : FIC_MINISBLACK;
+				}
+				return FIC_MINISBLACK;
+			}
+			break;
+			case FIT_RGB16:
+			case FIT_RGBF:
+				return FIC_RGB;
+			case FIT_RGBA16:
+			case FIT_RGBAF:
+				return FIC_RGBALPHA;
+		}
+
+		return FIC_MINISBLACK;
+	}
+
+	// standard image type
+	switch (FreeImage_GetBPP(dib)) {
+		case 1:
+		{
+			rgb = FreeImage_GetPalette(dib);
+
+			if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) {
+				rgb++;
+
+				if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) {
+					return FIC_MINISBLACK;
+				}
+			}
+
+			if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) {
+				rgb++;
+
+				if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) {
+					return FIC_MINISWHITE;
+				}
+			}
+
+			return FIC_PALETTE;
+		}
+
+		case 4:
+		case 8:	// Check if the DIB has a color or a greyscale palette
+		{
+			int ncolors = FreeImage_GetColorsUsed(dib);
+		    int minisblack = 1;
+			rgb = FreeImage_GetPalette(dib);
+
+			for (int i = 0; i < ncolors; i++) {
+				if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) {
+					return FIC_PALETTE;
+				}
+
+				// The DIB has a color palette if the greyscale isn't a linear ramp
+				// Take care of reversed grey images
+				if (rgb->rgbRed != i) {
+					if ((ncolors-i-1) != rgb->rgbRed) {
+						return FIC_PALETTE;
+					} else {
+						minisblack = 0;
+					}
+				}
+
+				rgb++;
+			}
+
+			return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE;
+		}
+
+		case 16:
+		case 24:
+			return FIC_RGB;
+
+		case 32:
+		{
+			if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) {
+				return FIC_CMYK;
+			}
+
+			if( FreeImage_HasPixels(dib) ) {
+				// check for fully opaque alpha layer
+				for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+					rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
+
+					for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+						if (rgb[x].rgbReserved != 0xFF) {
+							return FIC_RGBALPHA;
+						}
+					}
+				}
+				return FIC_RGB;
+			}
+
+			return FIC_RGBALPHA;
+		}
+				
+		default :
+			return FIC_MINISBLACK;
+	}
+}
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_TYPE DLL_CALLCONV 
+FreeImage_GetImageType(FIBITMAP *dib) {
+	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->type : FIT_UNKNOWN;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV 
+FreeImage_HasPixels(FIBITMAP *dib) {
+	return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->has_pixels : FALSE;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_HasRGBMasks(FIBITMAP *dib) {
+	return dib && FreeImage_GetInfoHeader(dib)->biCompression == BI_BITFIELDS;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetRedMask(FIBITMAP *dib) {
+	FREEIMAGERGBMASKS *masks = NULL;
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	switch(image_type) {
+		case FIT_BITMAP:
+			// check for 16-bit RGB (565 or 555)
+			masks = FreeImage_GetRGBMasks(dib);
+			if (masks) {
+				return masks->red_mask;
+			}
+			return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_RED_MASK : 0;
+		default:
+			return 0;
+	}
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetGreenMask(FIBITMAP *dib) {
+	FREEIMAGERGBMASKS *masks = NULL;
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	switch(image_type) {
+		case FIT_BITMAP:
+			// check for 16-bit RGB (565 or 555)
+			masks = FreeImage_GetRGBMasks(dib);
+			if (masks) {
+				return masks->green_mask;
+			}
+			return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_GREEN_MASK : 0;
+		default:
+			return 0;
+	}
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetBlueMask(FIBITMAP *dib) {
+	FREEIMAGERGBMASKS *masks = NULL;
+	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	switch(image_type) {
+		case FIT_BITMAP:
+			// check for 16-bit RGB (565 or 555)
+			masks = FreeImage_GetRGBMasks(dib);
+			if (masks) {
+				return masks->blue_mask;
+			}
+			return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_BLUE_MASK : 0;
+		default:
+			return 0;
+	}
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_HasBackgroundColor(FIBITMAP *dib) {
+	if(dib) {
+		RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+		return (bkgnd_color->rgbReserved != 0) ? TRUE : FALSE;
+	}
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
+	if(dib && bkcolor) {
+		if(FreeImage_HasBackgroundColor(dib)) {
+			// get the background color
+			RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+			memcpy(bkcolor, bkgnd_color, sizeof(RGBQUAD));
+			// get the background index
+			if(FreeImage_GetBPP(dib) == 8) {
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+					if(bkgnd_color->rgbRed == pal[i].rgbRed) {
+						if(bkgnd_color->rgbGreen == pal[i].rgbGreen) {
+							if(bkgnd_color->rgbBlue == pal[i].rgbBlue) {
+								bkcolor->rgbReserved = (BYTE)i;
+								return TRUE;
+							}
+						}
+					}
+				}
+			}
+
+			bkcolor->rgbReserved = 0;
+
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
+	if(dib) {
+		RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+		if(bkcolor) {
+			// set the background color
+			memcpy(bkgnd_color, bkcolor, sizeof(RGBQUAD));
+			// enable the file background color
+			bkgnd_color->rgbReserved = 1;
+		} else {
+			// clear and disable the file background color
+			memset(bkgnd_color, 0, sizeof(RGBQUAD));
+		}
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_IsTransparent(FIBITMAP *dib) {
+	if(dib) {
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+		switch(image_type) {
+			case FIT_BITMAP:
+				if(FreeImage_GetBPP(dib) == 32) {
+					if(FreeImage_GetColorType(dib) == FIC_RGBALPHA) {
+						return TRUE;
+					}
+				} else {
+					return ((FREEIMAGEHEADER *)dib->data)->transparent ? TRUE : FALSE;
+				}
+				break;
+			case FIT_RGBA16:
+			case FIT_RGBAF:
+				return TRUE;
+			default:
+				break;
+		}
+	}
+	return FALSE;
+}
+
+BYTE * DLL_CALLCONV
+FreeImage_GetTransparencyTable(FIBITMAP *dib) {
+	return dib ? ((FREEIMAGEHEADER *)dib->data)->transparent_table : NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled) {
+	if (dib) {
+		if ((FreeImage_GetBPP(dib) <= 8) || (FreeImage_GetBPP(dib) == 32)) {
+			((FREEIMAGEHEADER *)dib->data)->transparent = enabled;
+		} else {
+			((FREEIMAGEHEADER *)dib->data)->transparent = FALSE;
+		}
+	}
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetTransparencyCount(FIBITMAP *dib) {
+	return dib ? ((FREEIMAGEHEADER *)dib->data)->transparency_count : 0;
+}
+
+void DLL_CALLCONV
+FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count) {
+	if (dib) {
+		count = MAX(0, MIN(count, 256));
+		if (FreeImage_GetBPP(dib) <= 8) {
+			((FREEIMAGEHEADER *)dib->data)->transparent = (count > 0) ? TRUE : FALSE;
+			((FREEIMAGEHEADER *)dib->data)->transparency_count = count;
+
+			if (table) {
+				memcpy(((FREEIMAGEHEADER *)dib->data)->transparent_table, table, count);
+			} else {
+				memset(((FREEIMAGEHEADER *)dib->data)->transparent_table, 0xff, count);
+			}
+		} 
+	}
+}
+
+/** @brief Sets the index of the palette entry to be used as transparent color
+ for the image specified. Does nothing on high color images. 
+ 
+ This method sets the index of the palette entry to be used as single transparent
+ color for the image specified. This works on palletised images only and does
+ nothing for high color images.
+ 
+ Although it is possible for palletised images to have more than one transparent
+ color, this method sets the palette entry specified as the single transparent
+ color for the image. All other colors will be set to be non-transparent by this
+ method.
+ 
+ As with FreeImage_SetTransparencyTable(), this method also sets the image's
+ transparency property to TRUE (as it is set and obtained by
+ FreeImage_SetTransparent() and FreeImage_IsTransparent() respectively) for
+ palletised images.
+ 
+ @param dib Input image, whose transparent color is to be set.
+ @param index The index of the palette entry to be set as transparent color.
+ */
+void DLL_CALLCONV
+FreeImage_SetTransparentIndex(FIBITMAP *dib, int index) {
+	if (dib) {
+		int count = FreeImage_GetColorsUsed(dib);
+		if (count) {
+			BYTE *new_tt = (BYTE *)malloc(count * sizeof(BYTE));
+			memset(new_tt, 0xFF, count);
+			if ((index >= 0) && (index < count)) {
+				new_tt[index] = 0x00;
+			}
+			FreeImage_SetTransparencyTable(dib, new_tt, count);
+			free(new_tt);
+		}
+	}
+}
+
+/** @brief Returns the palette entry used as transparent color for the image
+ specified. Works for palletised images only and returns -1 for high color
+ images or if the image has no color set to be transparent. 
+ 
+ Although it is possible for palletised images to have more than one transparent
+ color, this function always returns the index of the first palette entry, set
+ to be transparent. 
+ 
+ @param dib Input image, whose transparent color is to be returned.
+ @return Returns the index of the palette entry used as transparent color for
+ the image specified or -1 if there is no transparent color found (e.g. the image
+ is a high color image).
+ */
+int DLL_CALLCONV
+FreeImage_GetTransparentIndex(FIBITMAP *dib) {
+	int count = FreeImage_GetTransparencyCount(dib);
+	BYTE *tt = FreeImage_GetTransparencyTable(dib);
+	for (int i = 0; i < count; i++) {
+		if (tt[i] == 0) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+// ----------------------------------------------------------
+
+FIICCPROFILE * DLL_CALLCONV
+FreeImage_GetICCProfile(FIBITMAP *dib) {
+	FIICCPROFILE *profile = (dib) ? (FIICCPROFILE *)&((FREEIMAGEHEADER *)dib->data)->iccProfile : NULL;
+	return profile;
+}
+
+FIICCPROFILE * DLL_CALLCONV
+FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size) {
+	// clear the profile but preserve profile->flags
+	FreeImage_DestroyICCProfile(dib);
+	// create the new profile
+	FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
+	if(size && profile) {
+		profile->data = malloc(size);
+		if(profile->data) {
+			memcpy(profile->data, data, profile->size = size);
+		}
+	}
+	return profile;
+}
+
+void DLL_CALLCONV
+FreeImage_DestroyICCProfile(FIBITMAP *dib) {
+	FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
+	if(profile) {
+		if (profile->data) {
+			free (profile->data);
+		}
+		// clear the profile but preserve profile->flags
+		profile->data = NULL;
+		profile->size = 0;
+	}
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetWidth(FIBITMAP *dib) {
+	return dib ? FreeImage_GetInfoHeader(dib)->biWidth : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetHeight(FIBITMAP *dib) {
+	return (dib) ? FreeImage_GetInfoHeader(dib)->biHeight : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetBPP(FIBITMAP *dib) {
+	return dib ? FreeImage_GetInfoHeader(dib)->biBitCount : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetLine(FIBITMAP *dib) {
+	return dib ? ((FreeImage_GetWidth(dib) * FreeImage_GetBPP(dib)) + 7) / 8 : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetPitch(FIBITMAP *dib) {
+	if(dib) {
+		FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)dib->data;
+		return fih->external_bits ? fih->external_pitch : (FreeImage_GetLine(dib) + 3 & ~3);
+	}
+	return 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetColorsUsed(FIBITMAP *dib) {
+	return dib ? FreeImage_GetInfoHeader(dib)->biClrUsed : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDIBSize(FIBITMAP *dib) {
+	return (dib) ? sizeof(BITMAPINFOHEADER) + (FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)) + (FreeImage_GetPitch(dib) * FreeImage_GetHeight(dib)) : 0;
+}
+
+RGBQUAD * DLL_CALLCONV
+FreeImage_GetPalette(FIBITMAP *dib) {
+	return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDotsPerMeterX(FIBITMAP *dib) {
+	return (dib) ? FreeImage_GetInfoHeader(dib)->biXPelsPerMeter : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDotsPerMeterY(FIBITMAP *dib) {
+	return (dib) ? FreeImage_GetInfoHeader(dib)->biYPelsPerMeter : 0;
+}
+
+void DLL_CALLCONV
+FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res) {
+	if(dib) {
+		FreeImage_GetInfoHeader(dib)->biXPelsPerMeter = res;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) {
+	if(dib) {
+		FreeImage_GetInfoHeader(dib)->biYPelsPerMeter = res;
+	}
+}
+
+BITMAPINFOHEADER * DLL_CALLCONV
+FreeImage_GetInfoHeader(FIBITMAP *dib) {
+	if(!dib) {
+		return NULL;
+	}
+	size_t lp = (size_t)dib->data + sizeof(FREEIMAGEHEADER);
+	lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
+	lp += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT;
+	return (BITMAPINFOHEADER *)lp;
+}
+
+BITMAPINFO * DLL_CALLCONV
+FreeImage_GetInfo(FIBITMAP *dib) {
+	return (BITMAPINFO *)FreeImage_GetInfoHeader(dib);
+}
+
+// ----------------------------------------------------------
+//  Metadata routines
+// ----------------------------------------------------------
+
+FIMETADATA * DLL_CALLCONV 
+FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) {
+	if(!dib) {
+		return NULL;
+	}
+
+	// get the metadata model
+	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+	TAGMAP *tagmap = NULL;
+	if( (*metadata).find(model) != (*metadata).end() ) {
+		tagmap = (*metadata)[model];
+	}
+	if(tagmap) {
+		// allocate a handle
+		FIMETADATA 	*handle = (FIMETADATA *)malloc(sizeof(FIMETADATA));
+		if(handle) {
+			// calculate the size of a METADATAHEADER
+			int header_size = sizeof(METADATAHEADER);
+
+			handle->data = (BYTE *)malloc(header_size * sizeof(BYTE));
+			
+			if(handle->data) {
+				memset(handle->data, 0, header_size * sizeof(BYTE));
+
+				// write out the METADATAHEADER
+				METADATAHEADER *mdh = (METADATAHEADER *)handle->data;
+
+				mdh->pos = 1;
+				mdh->tagmap = tagmap;
+
+				// get the first element
+				TAGMAP::iterator i = tagmap->begin();
+				*tag = (*i).second;
+
+				return handle;
+			}
+
+			free(handle);
+		}
+	}
+
+	return NULL;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) {
+	if(!mdhandle) {
+		return FALSE;
+	}
+
+	METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data;
+	TAGMAP *tagmap = mdh->tagmap;
+
+	int current_pos = mdh->pos;
+	int mapsize     = (int)tagmap->size();
+
+	if(current_pos < mapsize) {
+		// get the tag element at position pos
+		int count = 0;
+
+		for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
+			if(count == current_pos) {
+				*tag = (*i).second;
+				mdh->pos++;
+				break;
+			}
+			count++;
+		}
+		
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void DLL_CALLCONV 
+FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) {
+	if (NULL != mdhandle) {	// delete the handle
+		if (NULL != mdhandle->data) {
+			free(mdhandle->data);
+		}
+		free(mdhandle);		// ... and the wrapper
+	}
+}
+
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) {
+	if(!src || !dst) {
+		return FALSE;
+	}
+
+	// get metadata links
+	METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata;
+	METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata;
+
+	// copy metadata models, *except* the FIMD_ANIMATION model
+	for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
+		int model = (*i).first;
+		if(model == (int)FIMD_ANIMATION) {
+			continue;
+		}
+		TAGMAP *src_tagmap = (*i).second;
+
+		if(src_tagmap) {
+			if( dst_metadata->find(model) != dst_metadata->end() ) {
+				// destroy dst model
+				FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL);
+			}
+
+			// create a metadata model
+			TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
+
+			if(dst_tagmap) {
+				// fill the model
+				for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
+					std::string dst_key = (*j).first;
+					FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
+
+					// assign key and tag value
+					(*dst_tagmap)[dst_key] = dst_tag;
+				}
+
+				// assign model and tagmap
+				(*dst_metadata)[model] = dst_tagmap;
+			}
+		}
+	}
+
+	// clone resolution 
+	FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); 
+	FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); 
+
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV 
+FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
+	if(!dib) {
+		return FALSE;
+	}
+
+	TAGMAP *tagmap = NULL;
+
+	// get the metadata model
+	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+	METADATAMAP::iterator model_iterator = metadata->find(model);
+	if (model_iterator != metadata->end()) {
+		tagmap = model_iterator->second;
+	}
+
+	if(key != NULL) {
+
+		if(!tagmap) {
+			// this model, doesn't exist: create it 
+			tagmap = new(std::nothrow) TAGMAP();
+			(*metadata)[model] = tagmap;
+		}
+		
+		if(tag) {
+			// first check the tag
+			if(FreeImage_GetTagKey(tag) == NULL) {
+				FreeImage_SetTagKey(tag, key);
+			} else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
+				// set the tag key
+				FreeImage_SetTagKey(tag, key);
+			}
+			if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
+				FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key);
+				return FALSE;
+			}
+
+			// fill the tag ID if possible and if it's needed
+			TagLib& tag_lib = TagLib::instance();
+			switch(model) {
+				case FIMD_IPTC:
+				{
+					int id = tag_lib.getTagID(TagLib::IPTC, key);
+					/*
+					if(id == -1) {
+						FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key);
+					}
+					*/
+					FreeImage_SetTagID(tag, (WORD)id);
+				}
+				break;
+
+				default:
+					break;
+			}
+
+			// delete existing tag
+			FITAG *old_tag = (*tagmap)[key];
+			if(old_tag) {
+				FreeImage_DeleteTag(old_tag);
+			}
+
+			// create a new tag
+			(*tagmap)[key] = FreeImage_CloneTag(tag);
+		}
+		else {
+			// delete existing tag
+			TAGMAP::iterator i = tagmap->find(key);
+			if(i != tagmap->end()) {
+				FITAG *old_tag = (*i).second;
+				FreeImage_DeleteTag(old_tag);
+				tagmap->erase(key);
+			}
+		}
+	}
+	else {
+		// destroy the metadata model
+		if(tagmap) {
+			for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
+				FITAG *tag = (*i).second;
+				FreeImage_DeleteTag(tag);
+			}
+
+			delete tagmap;
+			metadata->erase(model_iterator);
+		}
+	}
+
+	return TRUE;
+}
+
+BOOL DLL_CALLCONV 
+FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) {
+	if(!dib || !key || !tag) {
+		return FALSE;
+	}
+
+	TAGMAP *tagmap = NULL;
+	*tag = NULL;
+
+	// get the metadata model
+	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+	if(!(*metadata).empty()) {
+		METADATAMAP::iterator model_iterator = metadata->find(model);
+		if (model_iterator != metadata->end() ) {
+			// this model exists : try to get the requested tag
+			tagmap = model_iterator->second;
+			TAGMAP::iterator tag_iterator = tagmap->find(key);
+			if (tag_iterator != tagmap->end() ) {
+				// get the requested tag
+				*tag = tag_iterator->second;
+			} 
+		}
+	}
+
+	return (*tag != NULL) ? TRUE : FALSE;
+}
+
+/**
+Build and set a FITAG whose type is FIDT_ASCII. 
+@param model Metadata model to be filled
+@param dib Image to be filled
+@param key Tag key
+@param value Tag value as a ASCII string
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL DLL_CALLCONV 
+FreeImage_SetMetadataKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
+	if(!dib || !key || !value) {
+		return FALSE;
+	}
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		BOOL bSuccess = TRUE;
+		// fill the tag
+		DWORD tag_length = (DWORD)(strlen(value) + 1);
+		bSuccess &= FreeImage_SetTagKey(tag, key);
+		bSuccess &= FreeImage_SetTagLength(tag, tag_length);
+		bSuccess &= FreeImage_SetTagCount(tag, tag_length);
+		bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII);
+		bSuccess &= FreeImage_SetTagValue(tag, value);
+		if(bSuccess) {
+			// set the tag
+			bSuccess &= FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag);
+		}
+		// delete the tag
+		FreeImage_DeleteTag(tag);
+
+		return bSuccess;
+	}
+
+	return FALSE;
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV 
+FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) {
+	if(!dib) {
+		return FALSE;
+	}
+
+	TAGMAP *tagmap = NULL;
+
+	// get the metadata model
+	METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+	if( (*metadata).find(model) != (*metadata).end() ) {
+		tagmap = (*metadata)[model];
+	}
+	if(!tagmap) {
+		// this model, doesn't exist: return
+		return 0;
+	}
+
+	// get the tag count
+	return (unsigned)tagmap->size();
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetMemorySize(FIBITMAP *dib) {
+	if (!dib) {
+		return 0;
+	}
+	FREEIMAGEHEADER *header = (FREEIMAGEHEADER *)dib->data;
+	BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(dib);
+
+	BOOL header_only = !header->has_pixels || header->external_bits != NULL;
+	BOOL need_masks = bih->biCompression == BI_BITFIELDS;
+	unsigned width = bih->biWidth;
+	unsigned height = bih->biHeight;
+	unsigned bpp = bih->biBitCount;
+	
+	// start off with the size of the FIBITMAP structure
+	size_t size = sizeof(FIBITMAP);
+	
+	// add sizes of FREEIMAGEHEADER, BITMAPINFOHEADER, palette and DIB data
+	size += FreeImage_GetInternalImageSize(header_only, width, height, bpp, need_masks);
+
+	// add ICC profile size
+	size += header->iccProfile.size;
+
+	// add thumbnail image size
+	if (header->thumbnail) {
+		// we assume a thumbnail not having a thumbnail as well, 
+		// so this recursive call should not create an infinite loop
+		size += FreeImage_GetMemorySize(header->thumbnail);
+	}
+
+	// add metadata size
+	METADATAMAP *md = header->metadata;
+	if (!md) {
+		return (unsigned)size;
+	}
+
+	// add size of METADATAMAP
+	size += sizeof(METADATAMAP);
+
+	const size_t models = md->size();
+	if (models == 0) {
+		return (unsigned)size;
+	}
+
+	unsigned tags = 0;
+
+	for (METADATAMAP::iterator i = md->begin(); i != md->end(); i++) {
+		TAGMAP *tm = i->second;
+		if (tm) {
+			for (TAGMAP::iterator j = tm->begin(); j != tm->end(); j++) {
+				++tags;
+				const std::string & key = j->first;
+				size += key.capacity();
+				size += FreeImage_GetTagMemorySize(j->second);
+			}
+		}
+	}
+
+	// add size of all TAGMAP instances
+	size += models * sizeof(TAGMAP);
+	// add size of tree nodes in METADATAMAP
+	size += MapIntrospector<METADATAMAP>::GetNodesMemorySize(models);
+	// add size of tree nodes in TAGMAP
+	size += MapIntrospector<TAGMAP>::GetNodesMemorySize(tags);
+
+	return (unsigned)size;
+}
+
diff --git a/files/Source/FreeImage/CacheFile.cpp b/files/Source/FreeImage/CacheFile.cpp
new file mode 100644
index 0000000..309faf8
--- /dev/null
+++ b/files/Source/FreeImage/CacheFile.cpp
@@ -0,0 +1,271 @@
+// ==========================================================
+// Multi-Page functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - checkered (checkered@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif 
+
+#include "CacheFile.h"
+
+// ----------------------------------------------------------
+
+CacheFile::CacheFile(const std::string filename, BOOL keep_in_memory) :
+m_file(NULL),
+m_filename(filename),
+m_free_pages(),
+m_page_cache_mem(),
+m_page_cache_disk(),
+m_page_map(),
+m_page_count(0),
+m_current_block(NULL),
+m_keep_in_memory(keep_in_memory) {
+}
+
+CacheFile::~CacheFile() {
+}
+
+BOOL
+CacheFile::open() {
+	if ((!m_filename.empty()) && (!m_keep_in_memory)) {
+		m_file = fopen(m_filename.c_str(), "w+b");
+		return (m_file != NULL);
+	}
+
+	return (m_keep_in_memory == TRUE);
+}
+
+void
+CacheFile::close() {
+	// dispose the cache entries
+
+	while (!m_page_cache_disk.empty()) {
+		Block *block = *m_page_cache_disk.begin();
+		m_page_cache_disk.pop_front();
+		delete [] block->data;
+		delete block;
+	}
+	while (!m_page_cache_mem.empty()) { 
+		Block *block = *m_page_cache_mem.begin(); 
+		m_page_cache_mem.pop_front(); 
+		delete [] block->data; 
+		delete block; 
+	} 
+
+	if (m_file) {
+		// close the file
+
+		fclose(m_file);
+
+		// delete the file
+
+		remove(m_filename.c_str());
+	}
+}
+
+void
+CacheFile::cleanupMemCache() {
+	if (!m_keep_in_memory) {
+		if (m_page_cache_mem.size() > CACHE_SIZE) {
+			// flush the least used block to file
+
+			Block *old_block = m_page_cache_mem.back();
+			fseek(m_file, old_block->nr * BLOCK_SIZE, SEEK_SET);
+			fwrite(old_block->data, BLOCK_SIZE, 1, m_file);
+
+			// remove the data
+
+			delete [] old_block->data;
+			old_block->data = NULL;
+
+			// move the block to another list
+
+			m_page_cache_disk.splice(m_page_cache_disk.begin(), m_page_cache_mem, --m_page_cache_mem.end());
+			m_page_map[old_block->nr] = m_page_cache_disk.begin();
+		}
+	}
+}
+
+int
+CacheFile::allocateBlock() {
+	Block *block = new Block;
+	block->data = new BYTE[BLOCK_SIZE];
+	block->next = 0;
+
+	if (!m_free_pages.empty()) {
+		block->nr = *m_free_pages.begin();
+		m_free_pages.pop_front();
+	} else {
+		block->nr = m_page_count++;
+	}
+
+	m_page_cache_mem.push_front(block);
+	m_page_map[block->nr] = m_page_cache_mem.begin();
+
+	cleanupMemCache();
+
+	return block->nr;
+}
+
+Block *
+CacheFile::lockBlock(int nr) {
+	if (m_current_block == NULL) {
+		PageMapIt it = m_page_map.find(nr);
+
+		if (it != m_page_map.end()) {
+			m_current_block = *(it->second);
+
+			// the block is swapped out to disc. load it back
+			// and remove the block from the cache. it might get cached
+			// again as soon as the memory buffer fills up
+
+			if (m_current_block->data == NULL) {
+				m_current_block->data = new BYTE[BLOCK_SIZE];
+
+				fseek(m_file, m_current_block->nr * BLOCK_SIZE, SEEK_SET);
+				fread(m_current_block->data, BLOCK_SIZE, 1, m_file);
+
+				m_page_cache_mem.splice(m_page_cache_mem.begin(), m_page_cache_disk, it->second);
+				m_page_map[nr] = m_page_cache_mem.begin();
+			}
+
+			// if the memory cache size is too large, swap an item to disc
+
+			cleanupMemCache();
+
+			// return the current block
+
+			return m_current_block;
+		}
+	}
+
+	return NULL;
+}
+
+BOOL
+CacheFile::unlockBlock(int nr) {
+	if (m_current_block) {
+		m_current_block = NULL;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL
+CacheFile::deleteBlock(int nr) {
+	if (!m_current_block) {
+		PageMapIt it = m_page_map.find(nr);
+
+		// remove block from cache
+
+		if (it != m_page_map.end())
+			m_page_map.erase(nr);
+
+		// add block to free page list
+
+		m_free_pages.push_back(nr);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL
+CacheFile::readFile(BYTE *data, int nr, int size) {
+	if ((data) && (size > 0)) {
+		int s = 0;
+		int block_nr = nr;
+
+		do {
+			int copy_nr = block_nr;
+
+			Block *block = lockBlock(copy_nr);
+
+			block_nr = block->next;
+
+			memcpy(data + s, block->data, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
+
+			unlockBlock(copy_nr);
+
+			s += BLOCK_SIZE;
+		} while (block_nr != 0);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+int
+CacheFile::writeFile(BYTE *data, int size) {
+	if ((data) && (size > 0)) {
+		int nr_blocks_required = 1 + (size / BLOCK_SIZE);
+		int count = 0;
+		int s = 0;
+		int stored_alloc;
+		int alloc;
+		
+		stored_alloc = alloc = allocateBlock();
+
+		do {
+			int copy_alloc = alloc;
+
+			Block *block = lockBlock(copy_alloc);
+
+			block->next = 0;
+
+			memcpy(block->data, data + s, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
+
+			if (count + 1 < nr_blocks_required)
+				alloc = block->next = allocateBlock();
+
+			unlockBlock(copy_alloc);
+
+			s += BLOCK_SIZE;			
+		} while (++count < nr_blocks_required);
+
+		return stored_alloc;
+	}
+
+	return 0;
+}
+
+void
+CacheFile::deleteFile(int nr) {
+	do {
+		Block *block = lockBlock(nr);
+
+		if (block == NULL)
+			break;
+
+		int next = block->next;
+
+		unlockBlock(nr);
+
+		deleteBlock(nr);
+
+		nr = next;
+	} while (nr != 0);
+}
+
diff --git a/files/Source/FreeImage/ColorLookup.cpp b/files/Source/FreeImage/ColorLookup.cpp
new file mode 100644
index 0000000..0f4435a
--- /dev/null
+++ b/files/Source/FreeImage/ColorLookup.cpp
@@ -0,0 +1,785 @@
+// ==========================================================
+// X11 and SVG Color name lookup
+//
+// Design and implementation by
+// - Karl-Heinz Bussian (khbussian@moss.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+//
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// RGB color names  ---------------------------------------------------------
+
+typedef struct tagNamedColor {
+	const char *name;	//! color name
+	BYTE  r;			//! red value
+	BYTE  g;			//! green value
+	BYTE  b;			//! blue value
+} NamedColor;
+
+// --------------------------------------------------------------------------
+
+/**
+Helper function : perform a binary search on a color array
+@param name Color name
+@param color_array Color array
+@param n Length of the color array
+@return Returns the color index in the array if successful, returns -1 otherwise
+*/
+static int
+binsearch(const char *name, const NamedColor *color_array, int n) {
+	int cond, low, mid, high;
+
+    low = 0;
+    high = n - 1;
+    while (low <= high) {
+		mid = (low + high) / 2;
+		if ((cond = strcmp(name, color_array[mid].name)) < 0) {
+			high = mid - 1;
+		} else if (cond > 0) {
+			low = mid + 1;
+		} else {
+			return mid;
+		}
+	}
+	return -1;
+}
+
+/**
+Perform a binary search on a color array
+@param szColor Color name
+@param color_array Color array
+@param ncolors Length of the color array
+@return Returns the color index in the array if successful, returns -1 otherwise
+*/
+static int
+FreeImage_LookupNamedColor(const char *szColor, const NamedColor *color_array, int ncolors) {
+	int i;
+    char color[64];
+
+    // make lower case name, squezze white space
+
+    for (i = 0; szColor[i] && i < sizeof(color) - 1; i++) {
+		if (isspace(szColor[i])) {
+            continue;
+		}
+		if (isupper(szColor[i])) {
+			color[i] = (char)tolower(szColor[i]);
+		} else {
+            color[i] = szColor[i];
+		}
+    }
+    color[i] = 0;
+
+    return binsearch(color, color_array, ncolors);
+}
+
+// ==========================================================
+// X11 Color name lookup
+
+/**
+ This big list of color names was formed from the file: /usr/X11R6/lib/X11/rgb.txt
+ found on a standard Linux installation.
+*/
+
+static NamedColor X11ColorMap[] = {
+    { "aliceblue",            240, 248, 255 },
+    { "antiquewhite",         250, 235, 215 },
+    { "antiquewhite1",        255, 239, 219 },
+    { "antiquewhite2",        238, 223, 204 },
+    { "antiquewhite3",        205, 192, 176 },
+    { "antiquewhite4",        139, 131, 120 },
+    { "aquamarine",           127, 255, 212 },
+    { "aquamarine1",          127, 255, 212 },
+    { "aquamarine2",          118, 238, 198 },
+    { "aquamarine3",          102, 205, 170 },
+    { "aquamarine4",           69, 139, 116 },
+    { "azure",                240, 255, 255 },
+    { "azure1",               240, 255, 255 },
+    { "azure2",               224, 238, 238 },
+    { "azure3",               193, 205, 205 },
+    { "azure4",               131, 139, 139 },
+    { "beige",                245, 245, 220 },
+    { "bisque",               255, 228, 196 },
+    { "bisque1",              255, 228, 196 },
+    { "bisque2",              238, 213, 183 },
+    { "bisque3",              205, 183, 158 },
+    { "bisque4",              139, 125, 107 },
+    { "black",                  0,   0,   0 },
+    { "blanchedalmond",       255, 235, 205 },
+    { "blue",                   0,   0, 255 },
+    { "blue1",                  0,   0, 255 },
+    { "blue2",                  0,   0, 238 },
+    { "blue3",                  0,   0, 205 },
+    { "blue4",                  0,   0, 139 },
+    { "blueviolet",           138,  43, 226 },
+    { "brown",                165,  42,  42 },
+    { "brown1",               255,  64,  64 },
+    { "brown2",               238,  59,  59 },
+    { "brown3",               205,  51,  51 },
+    { "brown4",               139,  35,  35 },
+    { "burlywood",            222, 184, 135 },
+    { "burlywood1",           255, 211, 155 },
+    { "burlywood2",           238, 197, 145 },
+    { "burlywood3",           205, 170, 125 },
+    { "burlywood4",           139, 115,  85 },
+    { "cadetblue",             95, 158, 160 },
+    { "cadetblue1",           152, 245, 255 },
+    { "cadetblue2",           142, 229, 238 },
+    { "cadetblue3",           122, 197, 205 },
+    { "cadetblue4",            83, 134, 139 },
+    { "chartreuse",           127, 255,   0 },
+    { "chartreuse1",          127, 255,   0 },
+    { "chartreuse2",          118, 238,   0 },
+    { "chartreuse3",          102, 205,   0 },
+    { "chartreuse4",           69, 139,   0 },
+    { "chocolate",            210, 105,  30 },
+    { "chocolate1",           255, 127,  36 },
+    { "chocolate2",           238, 118,  33 },
+    { "chocolate3",           205, 102,  29 },
+    { "chocolate4",           139,  69,  19 },
+    { "coral",                255, 127,  80 },
+    { "coral1",               255, 114,  86 },
+    { "coral2",               238, 106,  80 },
+    { "coral3",               205,  91,  69 },
+    { "coral4",               139,  62,  47 },
+    { "cornflowerblue",       100, 149, 237 },
+    { "cornsilk",             255, 248, 220 },
+    { "cornsilk1",            255, 248, 220 },
+    { "cornsilk2",            238, 232, 205 },
+    { "cornsilk3",            205, 200, 177 },
+    { "cornsilk4",            139, 136, 120 },
+    { "cyan",                   0, 255, 255 },
+    { "cyan1",                  0, 255, 255 },
+    { "cyan2",                  0, 238, 238 },
+    { "cyan3",                  0, 205, 205 },
+    { "cyan4",                  0, 139, 139 },
+    { "darkblue",               0,   0, 139 },
+    { "darkcyan",               0, 139, 139 },
+    { "darkgoldenrod",        184, 134,  11 },
+    { "darkgoldenrod1",       255, 185,  15 },
+    { "darkgoldenrod2",       238, 173,  14 },
+    { "darkgoldenrod3",       205, 149,  12 },
+    { "darkgoldenrod4",       139, 101,   8 },
+    { "darkgreen",              0, 100,   0 },
+    { "darkkhaki",            189, 183, 107 },
+    { "darkmagenta",          139,   0, 139 },
+    { "darkolivegreen",        85, 107,  47 },
+    { "darkolivegreen1",      202, 255, 112 },
+    { "darkolivegreen2",      188, 238, 104 },
+    { "darkolivegreen3",      162, 205,  90 },
+    { "darkolivegreen4",      110, 139,  61 },
+    { "darkorange",           255, 140,   0 },
+    { "darkorange1",          255, 127,   0 },
+    { "darkorange2",          238, 118,   0 },
+    { "darkorange3",          205, 102,   0 },
+    { "darkorange4",          139,  69,   0 },
+    { "darkorchid",           153,  50, 204 },
+    { "darkorchid1",          191,  62, 255 },
+    { "darkorchid2",          178,  58, 238 },
+    { "darkorchid3",          154,  50, 205 },
+    { "darkorchid4",          104,  34, 139 },
+    { "darkred",              139,   0,   0 },
+    { "darksalmon",           233, 150, 122 },
+    { "darkseagreen",         143, 188, 143 },
+    { "darkseagreen1",        193, 255, 193 },
+    { "darkseagreen2",        180, 238, 180 },
+    { "darkseagreen3",        155, 205, 155 },
+    { "darkseagreen4",        105, 139, 105 },
+    { "darkslateblue",         72,  61, 139 },
+    { "darkslategray",         47,  79,  79 },
+    { "darkslategray1",       151, 255, 255 },
+    { "darkslategray2",       141, 238, 238 },
+    { "darkslategray3",       121, 205, 205 },
+    { "darkslategray4",        82, 139, 139 },
+    { "darkslategrey",         47,  79,  79 },
+    { "darkturquoise",          0, 206, 209 },
+    { "darkviolet",           148,   0, 211 },
+    { "deeppink",             255,  20, 147 },
+    { "deeppink1",            255,  20, 147 },
+    { "deeppink2",            238,  18, 137 },
+    { "deeppink3",            205,  16, 118 },
+    { "deeppink4",            139,  10,  80 },
+    { "deepskyblue",            0, 191, 255 },
+    { "deepskyblue1",           0, 191, 255 },
+    { "deepskyblue2",           0, 178, 238 },
+    { "deepskyblue3",           0, 154, 205 },
+    { "deepskyblue4",           0, 104, 139 },
+    { "dimgray",              105, 105, 105 },
+    { "dimgrey",              105, 105, 105 },
+    { "dodgerblue",            30, 144, 255 },
+    { "dodgerblue1",           30, 144, 255 },
+    { "dodgerblue2",           28, 134, 238 },
+    { "dodgerblue3",           24, 116, 205 },
+    { "dodgerblue4",           16,  78, 139 },
+    { "firebrick",            178,  34,  34 },
+    { "firebrick1",           255,  48,  48 },
+    { "firebrick2",           238,  44,  44 },
+    { "firebrick3",           205,  38,  38 },
+    { "firebrick4",           139,  26,  26 },
+    { "floralwhite",          255, 250, 240 },
+    { "forestgreen",          176,  48,  96 },
+    { "gainsboro",            220, 220, 220 },
+    { "ghostwhite",           248, 248, 255 },
+    { "gold",                 255, 215,   0 },
+    { "gold1",                255, 215,   0 },
+    { "gold2",                238, 201,   0 },
+    { "gold3",                205, 173,   0 },
+    { "gold4",                139, 117,   0 },
+    { "goldenrod",            218, 165,  32 },
+    { "goldenrod1",           255, 193,  37 },
+    { "goldenrod2",           238, 180,  34 },
+    { "goldenrod3",           205, 155,  29 },
+    { "goldenrod4",           139, 105,  20 },
+    { "gray",                 190, 190, 190 },
+    { "green",                  0, 255,   0 },
+    { "green1",                 0, 255,   0 },
+    { "green2",                 0, 238,   0 },
+    { "green3",                 0, 205,   0 },
+    { "green4",                 0, 139,   0 },
+    { "greenyellow",          173, 255,  47 },
+    { "grey",                 190, 190, 190 },
+    { "honeydew",             240, 255, 240 },
+    { "honeydew1",            240, 255, 240 },
+    { "honeydew2",            224, 238, 224 },
+    { "honeydew3",            193, 205, 193 },
+    { "honeydew4",            131, 139, 131 },
+    { "hotpink",              255, 105, 180 },
+    { "hotpink1",             255, 110, 180 },
+    { "hotpink2",             238, 106, 167 },
+    { "hotpink3",             205,  96, 144 },
+    { "hotpink4",             139,  58,  98 },
+    { "indianred",            205,  92,  92 },
+    { "indianred1",           255, 106, 106 },
+    { "indianred2",           238,  99,  99 },
+    { "indianred3",           205,  85,  85 },
+    { "indianred4",           139,  58,  58 },
+    { "ivory",                255, 255, 240 },
+    { "ivory1",               255, 255, 240 },
+    { "ivory2",               238, 238, 224 },
+    { "ivory3",               205, 205, 193 },
+    { "ivory4",               139, 139, 131 },
+    { "khaki",                240, 230, 140 },
+    { "khaki1",               255, 246, 143 },
+    { "khaki2",               238, 230, 133 },
+    { "khaki3",               205, 198, 115 },
+    { "khaki4",               139, 134,  78 },
+    { "lavender",             230, 230, 250 },
+    { "lavenderblush",        255, 240, 245 },
+    { "lavenderblush1",       255, 240, 245 },
+    { "lavenderblush2",       238, 224, 229 },
+    { "lavenderblush3",       205, 193, 197 },
+    { "lavenderblush4",       139, 131, 134 },
+    { "lawngreen",            124, 252,   0 },
+    { "lemonchiffon",         255, 250, 205 },
+    { "lemonchiffon1",        255, 250, 205 },
+    { "lemonchiffon2",        238, 233, 191 },
+    { "lemonchiffon3",        205, 201, 165 },
+    { "lemonchiffon4",        139, 137, 112 },
+    { "lightblue",            173, 216, 230 },
+    { "lightblue1",           191, 239, 255 },
+    { "lightblue2",           178, 223, 238 },
+    { "lightblue3",           154, 192, 205 },
+    { "lightblue4",           104, 131, 139 },
+    { "lightcoral",           240, 128, 128 },
+    { "lightcyan",            224, 255, 255 },
+    { "lightcyan1",           224, 255, 255 },
+    { "lightcyan2",           209, 238, 238 },
+    { "lightcyan3",           180, 205, 205 },
+    { "lightcyan4",           122, 139, 139 },
+    { "lightgoldenrod",       238, 221, 130 },
+    { "lightgoldenrod1",      255, 236, 139 },
+    { "lightgoldenrod2",      238, 220, 130 },
+    { "lightgoldenrod3",      205, 190, 112 },
+    { "lightgoldenrod4",      139, 129,  76 },
+    { "lightgoldenrodyellow", 250, 250, 210 },
+    { "lightgray",            211, 211, 211 },
+    { "lightgreen",           144, 238, 144 },
+    { "lightgrey",            211, 211, 211 },
+    { "lightpink",            255, 182, 193 },
+    { "lightpink1",           255, 174, 185 },
+    { "lightpink2",           238, 162, 173 },
+    { "lightpink3",           205, 140, 149 },
+    { "lightpink4",           139,  95, 101 },
+    { "lightsalmon",          255, 160, 122 },
+    { "lightsalmon1",         255, 160, 122 },
+    { "lightsalmon2",         238, 149, 114 },
+    { "lightsalmon3",         205, 129,  98 },
+    { "lightsalmon4",         139,  87,  66 },
+    { "lightseagreen",         32, 178, 170 },
+    { "lightskyblue",         135, 206, 250 },
+    { "lightskyblue1",        176, 226, 255 },
+    { "lightskyblue2",        164, 211, 238 },
+    { "lightskyblue3",        141, 182, 205 },
+    { "lightskyblue4",         96, 123, 139 },
+    { "lightslateblue",       132, 112, 255 },
+    { "lightslategray",       119, 136, 153 },
+    { "lightslategrey",       119, 136, 153 },
+    { "lightsteelblue",       176, 196, 222 },
+    { "lightsteelblue1",      202, 225, 255 },
+    { "lightsteelblue2",      188, 210, 238 },
+    { "lightsteelblue3",      162, 181, 205 },
+    { "lightsteelblue4",      110, 123, 139 },
+    { "lightyellow",          255, 255, 224 },
+    { "lightyellow1",         255, 255, 224 },
+    { "lightyellow2",         238, 238, 209 },
+    { "lightyellow3",         205, 205, 180 },
+    { "lightyellow4",         139, 139, 122 },
+    { "limegreen",             50, 205,  50 },
+    { "linen",                250, 240, 230 },
+    { "magenta",              255,   0, 255 },
+    { "magenta1",             255,   0, 255 },
+    { "magenta2",             238,   0, 238 },
+    { "magenta3",             205,   0, 205 },
+    { "magenta4",             139,   0, 139 },
+    { "maroon",                 0, 255, 255 },
+    { "maroon1",              255,  52, 179 },
+    { "maroon2",              238,  48, 167 },
+    { "maroon3",              205,  41, 144 },
+    { "maroon4",              139,  28,  98 },
+    { "mediumaquamarine",     102, 205, 170 },
+    { "mediumblue",             0,   0, 205 },
+    { "mediumorchid",         186,  85, 211 },
+    { "mediumorchid1",        224, 102, 255 },
+    { "mediumorchid2",        209,  95, 238 },
+    { "mediumorchid3",        180,  82, 205 },
+    { "mediumorchid4",        122,  55, 139 },
+    { "mediumpurple",         147, 112, 219 },
+    { "mediumpurple1",        171, 130, 255 },
+    { "mediumpurple2",        159, 121, 238 },
+    { "mediumpurple3",        137, 104, 205 },
+    { "mediumpurple4",         93,  71, 139 },
+    { "mediumseagreen",        60, 179, 113 },
+    { "mediumslateblue",      123, 104, 238 },
+    { "mediumspringgreen",      0, 250, 154 },
+    { "mediumturquoise",       72, 209, 204 },
+    { "mediumvioletred",      199,  21, 133 },
+    { "midnightblue",          25,  25, 112 },
+    { "mintcream",            245, 255, 250 },
+    { "mistyrose",            255, 228, 225 },
+    { "mistyrose1",           255, 228, 225 },
+    { "mistyrose2",           238, 213, 210 },
+    { "mistyrose3",           205, 183, 181 },
+    { "mistyrose4",           139, 125, 123 },
+    { "moccasin",             255, 228, 181 },
+    { "navajowhite",          255, 222, 173 },
+    { "navajowhite1",         255, 222, 173 },
+    { "navajowhite2",         238, 207, 161 },
+    { "navajowhite3",         205, 179, 139 },
+    { "navajowhite4",         139, 121,  94 },
+    { "navy",                   0,   0, 128 },
+    { "navyblue",               0,   0, 128 },
+    { "oldlace",              253, 245, 230 },
+    { "olivedrab",            107, 142,  35 },
+    { "olivedrab1",           192, 255,  62 },
+    { "olivedrab2",           179, 238,  58 },
+    { "olivedrab3",           154, 205,  50 },
+    { "olivedrab4",           105, 139,  34 },
+    { "orange",               255, 165,   0 },
+    { "orange1",              255, 165,   0 },
+    { "orange2",              238, 154,   0 },
+    { "orange3",              205, 133,   0 },
+    { "orange4",              139,  90,   0 },
+    { "orangered",            255,  69,   0 },
+    { "orangered1",           255,  69,   0 },
+    { "orangered2",           238,  64,   0 },
+    { "orangered3",           205,  55,   0 },
+    { "orangered4",           139,  37,   0 },
+    { "orchid",               218, 112, 214 },
+    { "orchid1",              255, 131, 250 },
+    { "orchid2",              238, 122, 233 },
+    { "orchid3",              205, 105, 201 },
+    { "orchid4",              139,  71, 137 },
+    { "palegoldenrod",        238, 232, 170 },
+    { "palegreen",            152, 251, 152 },
+    { "palegreen1",           154, 255, 154 },
+    { "palegreen2",           144, 238, 144 },
+    { "palegreen3",           124, 205, 124 },
+    { "palegreen4",            84, 139,  84 },
+    { "paleturquoise",        175, 238, 238 },
+    { "paleturquoise1",       187, 255, 255 },
+    { "paleturquoise2",       174, 238, 238 },
+    { "paleturquoise3",       150, 205, 205 },
+    { "paleturquoise4",       102, 139, 139 },
+    { "palevioletred",        219, 112, 147 },
+    { "palevioletred1",       255, 130, 171 },
+    { "palevioletred2",       238, 121, 159 },
+    { "palevioletred3",       205, 104, 137 },
+    { "palevioletred4",       139,  71,  93 },
+    { "papayawhip",           255, 239, 213 },
+    { "peachpuff",            255, 218, 185 },
+    { "peachpuff1",           255, 218, 185 },
+    { "peachpuff2",           238, 203, 173 },
+    { "peachpuff3",           205, 175, 149 },
+    { "peachpuff4",           139, 119, 101 },
+    { "peru",                 205, 133,  63 },
+    { "pink",                 255, 192, 203 },
+    { "pink1",                255, 181, 197 },
+    { "pink2",                238, 169, 184 },
+    { "pink3",                205, 145, 158 },
+    { "pink4",                139,  99, 108 },
+    { "plum",                 221, 160, 221 },
+    { "plum1",                255, 187, 255 },
+    { "plum2",                238, 174, 238 },
+    { "plum3",                205, 150, 205 },
+    { "plum4",                139, 102, 139 },
+    { "powderblue",           176, 224, 230 },
+    { "purple",               160,  32, 240 },
+    { "purple1",              155,  48, 255 },
+    { "purple2",              145,  44, 238 },
+    { "purple3",              125,  38, 205 },
+    { "purple4",               85,  26, 139 },
+    { "red",                  255,   0,   0 },
+    { "red1",                 255,   0,   0 },
+    { "red2",                 238,   0,   0 },
+    { "red3",                 205,   0,   0 },
+    { "red4",                 139,   0,   0 },
+    { "rosybrown",            188, 143, 143 },
+    { "rosybrown1",           255, 193, 193 },
+    { "rosybrown2",           238, 180, 180 },
+    { "rosybrown3",           205, 155, 155 },
+    { "rosybrown4",           139, 105, 105 },
+    { "royalblue",             65, 105, 225 },
+    { "royalblue1",            72, 118, 255 },
+    { "royalblue2",            67, 110, 238 },
+    { "royalblue3",            58,  95, 205 },
+    { "royalblue4",            39,  64, 139 },
+    { "saddlebrown",          139,  69,  19 },
+    { "salmon",               250, 128, 114 },
+    { "salmon1",              255, 140, 105 },
+    { "salmon2",              238, 130,  98 },
+    { "salmon3",              205, 112,  84 },
+    { "salmon4",              139,  76,  57 },
+    { "sandybrown",           244, 164,  96 },
+    { "seagreen",              46, 139,  87 },
+    { "seagreen1",             84, 255, 159 },
+    { "seagreen2",             78, 238, 148 },
+    { "seagreen3",             67, 205, 128 },
+    { "seagreen4",             46, 139,  87 },
+    { "seashell",             255, 245, 238 },
+    { "seashell1",            255, 245, 238 },
+    { "seashell2",            238, 229, 222 },
+    { "seashell3",            205, 197, 191 },
+    { "seashell4",            139, 134, 130 },
+    { "sienna",               160,  82,  45 },
+    { "sienna1",              255, 130,  71 },
+    { "sienna2",              238, 121,  66 },
+    { "sienna3",              205, 104,  57 },
+    { "sienna4",              139,  71,  38 },
+    { "skyblue",              135, 206, 235 },
+    { "skyblue1",             135, 206, 255 },
+    { "skyblue2",             126, 192, 238 },
+    { "skyblue3",             108, 166, 205 },
+    { "skyblue4",              74, 112, 139 },
+    { "slateblue",            106,  90, 205 },
+    { "slateblue1",           131, 111, 255 },
+    { "slateblue2",           122, 103, 238 },
+    { "slateblue3",           105,  89, 205 },
+    { "slateblue4",            71,  60, 139 },
+    { "slategray",            112, 128, 144 },
+    { "slategray1",           198, 226, 255 },
+    { "slategray2",           185, 211, 238 },
+    { "slategray3",           159, 182, 205 },
+    { "slategray4",           108, 123, 139 },
+    { "slategrey",            112, 128, 144 },
+    { "snow",                 255, 250, 250 },
+    { "snow1",                255, 250, 250 },
+    { "snow2",                238, 233, 233 },
+    { "snow3",                205, 201, 201 },
+    { "snow4",                139, 137, 137 },
+    { "springgreen",            0, 255, 127 },
+    { "springgreen1",           0, 255, 127 },
+    { "springgreen2",           0, 238, 118 },
+    { "springgreen3",           0, 205, 102 },
+    { "springgreen4",           0, 139,  69 },
+    { "steelblue",             70, 130, 180 },
+    { "steelblue1",            99, 184, 255 },
+    { "steelblue2",            92, 172, 238 },
+    { "steelblue3",            79, 148, 205 },
+    { "steelblue4",            54, 100, 139 },
+    { "tan",                  210, 180, 140 },
+    { "tan1",                 255, 165,  79 },
+    { "tan2",                 238, 154,  73 },
+    { "tan3",                 205, 133,  63 },
+    { "tan4",                 139,  90,  43 },
+    { "thistle",              216, 191, 216 },
+    { "thistle1",             255, 225, 255 },
+    { "thistle2",             238, 210, 238 },
+    { "thistle3",             205, 181, 205 },
+    { "thistle4",             139, 123, 139 },
+    { "tomato",               255,  99,  71 },
+    { "tomato1",              255,  99,  71 },
+    { "tomato2",              238,  92,  66 },
+    { "tomato3",              205,  79,  57 },
+    { "tomato4",              139,  54,  38 },
+    { "turquoise",             64, 224, 208 },
+    { "turquoise1",             0, 245, 255 },
+    { "turquoise2",             0, 229, 238 },
+    { "turquoise3",             0, 197, 205 },
+    { "turquoise4",             0, 134, 139 },
+    { "violet",               238, 130, 238 },
+    { "violetred",            208,  32, 144 },
+    { "violetred1",           255,  62, 150 },
+    { "violetred2",           238,  58, 140 },
+    { "violetred3",           205,  50, 120 },
+    { "violetred4",           139,  34,  82 },
+    { "wheat",                245, 222, 179 },
+    { "wheat1",               255, 231, 186 },
+    { "wheat2",               238, 216, 174 },
+    { "wheat3",               205, 186, 150 },
+    { "wheat4",               139, 126, 102 },
+    { "white",                255, 255, 255 },
+    { "whitesmoke",           245, 245, 245 },
+    { "yellow",               255, 255,   0 },
+    { "yellow1",              255, 255,   0 },
+    { "yellow2",              238, 238,   0 },
+    { "yellow3",              205, 205,   0 },
+    { "yellow4",              139, 139,   0 },
+    { "yellowgreen",          154, 205,  50 }
+};
+
+
+BOOL DLL_CALLCONV
+FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue) {
+    int i;
+
+    // lookup color
+    i = FreeImage_LookupNamedColor(szColor, X11ColorMap, sizeof(X11ColorMap)/sizeof(X11ColorMap[0]));
+    if (i >= 0) {
+        *nRed   = X11ColorMap[i].r;
+        *nGreen = X11ColorMap[i].g;
+        *nBlue  = X11ColorMap[i].b;
+        return TRUE;
+    }
+
+    // not found, try for grey color with attached percent value
+    if ( (szColor[0] == 'g' || szColor[0] == 'G') &&
+         (szColor[1] == 'r' || szColor[1] == 'R') &&
+         (szColor[2] == 'e' || szColor[2] == 'E' || szColor[2] == 'a' || szColor[2] == 'A' ) &&
+         (szColor[3] == 'y' || szColor[3] == 'Y' ) )  {
+
+        // grey<num>, or gray<num>, num 1...100
+        i = strtol(szColor+4, NULL, 10);
+        *nRed   = (BYTE)(255.0/100.0 * i);
+        *nGreen = *nRed;
+        *nBlue  = *nRed;
+
+        return TRUE;
+    }
+
+    // not found at all
+    *nRed   = 0;
+    *nGreen = 0;
+    *nBlue  = 0;
+
+    return FALSE;
+}
+
+// ==========================================================
+// SVG Color name lookup
+
+/**
+ These are the colors defined in the SVG standard (I haven't checked
+ the final recommendation for changes)
+*/
+static NamedColor SVGColorMap[] = {
+	{ "aliceblue",                  240, 248, 255 },
+	{ "antiquewhite",               250, 235, 215 },
+	{ "aqua",                         0, 255, 255 },
+	{ "aquamarine",                 127, 255, 212 },
+	{ "azure",                      240, 255, 255 },
+	{ "beige",                      245, 245, 220 },
+	{ "bisque",                     255, 228, 196 },
+	{ "black",                        0,   0,   0 },
+	{ "blanchedalmond",             255, 235, 205 },
+	{ "blue",                         0,   0, 255 },
+	{ "blueviolet",                 138,  43, 226 },
+	{ "brown",                      165,  42,  42 },
+	{ "burlywood",                  222, 184, 135 },
+	{ "cadetblue",                   95, 158, 160 },
+	{ "chartreuse",                 127, 255,   0 },
+	{ "chocolate",                  210, 105,  30 },
+	{ "coral",                      255, 127,  80 },
+	{ "cornflowerblue",             100, 149, 237 },
+	{ "cornsilk",                   255, 248, 220 },
+	{ "crimson",                    220,  20,  60 },
+	{ "cyan",                         0, 255, 255 },
+	{ "darkblue",                     0,   0, 139 },
+	{ "darkcyan",                     0, 139, 139 },
+	{ "darkgoldenrod",              184, 134,  11 },
+	{ "darkgray",                   169, 169, 169 },
+	{ "darkgreen",                    0, 100,   0 },
+	{ "darkgrey",                   169, 169, 169 },
+	{ "darkkhaki",                  189, 183, 107 },
+	{ "darkmagenta",                139,   0, 139 },
+	{ "darkolivegreen",              85, 107,  47 },
+	{ "darkorange",                 255, 140,   0 },
+	{ "darkorchid",                 153,  50, 204 },
+	{ "darkred",                    139,   0,   0 },
+	{ "darksalmon",                 233, 150, 122 },
+	{ "darkseagreen",               143, 188, 143 },
+	{ "darkslateblue",               72,  61, 139 },
+	{ "darkslategray",               47,  79,  79 },
+	{ "darkslategrey",               47,  79,  79 },
+	{ "darkturquoise",                0, 206, 209 },
+	{ "darkviolet",                 148,   0, 211 },
+	{ "deeppink",                   255,  20, 147 },
+	{ "deepskyblue",                  0, 191, 255 },
+	{ "dimgray",                    105, 105, 105 },
+	{ "dimgrey",                    105, 105, 105 },
+	{ "dodgerblue",                  30, 144, 255 },
+	{ "firebrick",                  178,  34,  34 },
+	{ "floralwhite",                255, 250, 240 },
+	{ "forestgreen",                 34, 139,  34 },
+	{ "fuchsia",                    255,   0, 255 },
+	{ "gainsboro",                  220, 220, 220 },
+	{ "ghostwhite",                 248, 248, 255 },
+	{ "gold",                       255, 215,   0 },
+	{ "goldenrod",                  218, 165,  32 },
+	{ "gray",                       128, 128, 128 },
+	{ "green",                        0, 128,   0 },
+	{ "greenyellow",                173, 255,  47 },
+	{ "grey",                       128, 128, 128 },
+	{ "honeydew",                   240, 255, 240 },
+	{ "hotpink",                    255, 105, 180 },
+	{ "indianred",                  205,  92,  92 },
+	{ "indigo",                      75,   0, 130 },
+	{ "ivory",                      255, 255, 240 },
+	{ "khaki",                      240, 230, 140 },
+	{ "lavender",                   230, 230, 250 },
+	{ "lavenderblush",              255, 240, 245 },
+	{ "lawngreen",                  124, 252,   0 },
+	{ "lemonchiffon",               255, 250, 205 },
+	{ "lightblue",                  173, 216, 230 },
+	{ "lightcoral",                 240, 128, 128 },
+	{ "lightcyan",                  224, 255, 255 },
+	{ "lightgoldenrodyellow",       250, 250, 210 },
+	{ "lightgray",                  211, 211, 211 },
+	{ "lightgreen",                 144, 238, 144 },
+	{ "lightgrey",                  211, 211, 211 },
+	{ "lightpink",                  255, 182, 193 },
+	{ "lightsalmon",                255, 160, 122 },
+	{ "lightseagreen",               32, 178, 170 },
+	{ "lightskyblue",               135, 206, 250 },
+	{ "lightslategray",             119, 136, 153 },
+	{ "lightslategrey",             119, 136, 153 },
+	{ "lightsteelblue",             176, 196, 222 },
+	{ "lightyellow",                255, 255, 224 },
+	{ "lime",                         0, 255,   0 },
+	{ "limegreen",                   50, 205,  50 },
+	{ "linen",                      250, 240, 230 },
+	{ "magenta",                    255,   0, 255 },
+	{ "maroon",                     128,   0,   0 },
+	{ "mediumaquamarine",           102, 205, 170 },
+	{ "mediumblue",                   0,   0, 205 },
+	{ "mediumorchid",               186,  85, 211 },
+	{ "mediumpurple",               147, 112, 219 },
+	{ "mediumseagreen",              60, 179, 113 },
+	{ "mediumslateblue",            123, 104, 238 },
+	{ "mediumspringgreen",            0, 250, 154 },
+	{ "mediumturquoise",             72, 209, 204 },
+	{ "mediumvioletred",            199,  21, 133 },
+	{ "midnightblue",                25,  25, 112 },
+	{ "mintcream",                  245, 255, 250 },
+	{ "mistyrose",                  255, 228, 225 },
+	{ "moccasin",                   255, 228, 181 },
+	{ "navajowhite",                255, 222, 173 },
+	{ "navy",                         0,   0, 128 },
+	{ "oldlace",                    253, 245, 230 },
+	{ "olive",                      128, 128,   0 },
+	{ "olivedrab",                  107, 142,  35 },
+	{ "orange",                     255, 165,   0 },
+	{ "orangered",                  255,  69,   0 },
+	{ "orchid",                     218, 112, 214 },
+	{ "palegoldenrod",              238, 232, 170 },
+	{ "palegreen",                  152, 251, 152 },
+	{ "paleturquoise",              175, 238, 238 },
+	{ "palevioletred",              219, 112, 147 },
+	{ "papayawhip",                 255, 239, 213 },
+	{ "peachpuff",                  255, 218, 185 },
+	{ "peru",                       205, 133,  63 },
+	{ "pink",                       255, 192, 203 },
+	{ "plum",                       221, 160, 221 },
+	{ "powderblue",                 176, 224, 230 },
+	{ "purple",                     128,   0, 128 },
+	{ "red",                        255,   0,   0 },
+	{ "rosybrown",                  188, 143, 143 },
+	{ "royalblue",                   65, 105, 225 },
+	{ "saddlebrown",                139,  69,  19 },
+	{ "salmon",                     250, 128, 114 },
+	{ "sandybrown",                 244, 164,  96 },
+	{ "seagreen",                    46, 139,  87 },
+	{ "seashell",                   255, 245, 238 },
+	{ "sienna",                     160,  82,  45 },
+	{ "silver",                     192, 192, 192 },
+	{ "skyblue",                    135, 206, 235 },
+	{ "slateblue",                  106,  90, 205 },
+	{ "slategray",                  112, 128, 144 },
+	{ "slategrey",                  112, 128, 144 },
+	{ "snow",                       255, 250, 250 },
+	{ "springgreen",                  0, 255, 127 },
+	{ "steelblue",                   70, 130, 180 },
+	{ "tan",                        210, 180, 140 },
+	{ "teal",                         0, 128, 128 },
+	{ "thistle",                    216, 191, 216 },
+	{ "tomato",                     255,  99,  71 },
+	{ "turquoise",                   64, 224, 208 },
+	{ "violet",                     238, 130, 238 },
+	{ "wheat",                      245, 222, 179 },
+	{ "white",                      255, 255, 255 },
+	{ "whitesmoke",                 245, 245, 245 },
+	{ "yellow",                     255, 255,   0 },
+	{ "yellowgreen",                154, 205,  50 }
+};
+
+
+BOOL DLL_CALLCONV
+FreeImage_LookupSVGColor(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue) {
+    int i;
+
+    // lookup color
+    i = FreeImage_LookupNamedColor(szColor, SVGColorMap, sizeof(SVGColorMap)/sizeof(SVGColorMap[0]));
+    if (i >= 0) {
+        *nRed   = SVGColorMap[i].r;
+        *nGreen = SVGColorMap[i].g;
+        *nBlue  = SVGColorMap[i].b;
+        return TRUE;
+    }
+
+    // not found, try for grey color with attached percent value
+    if ( (szColor[0] == 'g' || szColor[0] == 'G') &&
+         (szColor[1] == 'r' || szColor[1] == 'R') &&
+         (szColor[2] == 'e' || szColor[2] == 'E' || szColor[2] == 'a' || szColor[2] == 'A' ) &&
+         (szColor[3] == 'y' || szColor[3] == 'Y' ) )  {
+
+        // grey<num>, or gray<num>, num 1...100
+        i = strtol(szColor+4, NULL, 10);
+        *nRed   = (BYTE)(255.0/100.0 * i);
+        *nGreen = *nRed;
+        *nBlue  = *nRed;
+        return TRUE;
+    }
+
+    // not found at all
+    *nRed   = 0;
+    *nGreen = 0;
+    *nBlue  = 0;
+
+    return FALSE;
+}
+
diff --git a/files/Source/FreeImage/Conversion.cpp b/files/Source/FreeImage/Conversion.cpp
new file mode 100644
index 0000000..815057a
--- /dev/null
+++ b/files/Source/FreeImage/Conversion.cpp
@@ -0,0 +1,551 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "Quantizers.h"
+
+// ----------------------------------------------------------
+
+#define CONVERT(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib)); break;
+#define CONVERTWITHPALETTE(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); break;
+
+#define CONVERTTO16(from) \
+	case 16 : \
+		if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
+			FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib)); \
+		} else { \
+			FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib)); \
+		} \
+		break;
+
+#define CONVERTTO16WITHPALETTE(from) \
+	case 16 : \
+		if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
+			FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
+		} else { \
+			FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
+		} \
+		break;
+
+// ==========================================================
+// Utility functions declared in Utilities.h
+
+BOOL SwapRedBlue32(FIBITMAP* dib) {
+	if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
+		return FALSE;
+	}
+		
+	const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
+	if(bytesperpixel > 4 || bytesperpixel < 3) {
+		return FALSE;
+	}
+		
+	const unsigned height = FreeImage_GetHeight(dib);
+	const unsigned pitch = FreeImage_GetPitch(dib);
+	const unsigned lineSize = FreeImage_GetLine(dib);
+	
+	BYTE* line = FreeImage_GetBits(dib);
+	for(unsigned y = 0; y < height; ++y, line += pitch) {
+		for(BYTE* pixel = line; pixel < line + lineSize ; pixel += bytesperpixel) {
+			INPLACESWAP(pixel[0], pixel[2]);
+		}
+	}
+	
+	return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static inline void 
+assignRGB(WORD r, WORD g, WORD b, WORD* out) {
+	out[0] = r;
+	out[1] = g;
+	out[2] = b;
+}
+
+static inline void 
+assignRGB(BYTE r, BYTE g, BYTE b, BYTE* out) {
+	out[FI_RGBA_RED]	= r;
+	out[FI_RGBA_GREEN]	= g;
+	out[FI_RGBA_BLUE]	= b;
+}
+
+/**
+CMYK -> CMY -> RGB conversion from http://www.easyrgb.com/
+
+CMYK to CMY [0-1]: C,M,Y * (1 - K) + K
+CMY to RGB [0-1]: (1 - C,M,Y)
+
+=> R,G,B = (1 - C,M,Y) * (1 - K)
+mapped to [0-MAX_VAL]: 
+(MAX_VAL - C,M,Y) * (MAX_VAL - K) / MAX_VAL
+*/
+template <class T>
+static inline void 
+CMYKToRGB(T C, T M, T Y, T K, T* out) {
+	unsigned max_val = std::numeric_limits<T>::max();
+	
+	unsigned r = (max_val - C) * (max_val - K) / max_val;
+	unsigned g = (max_val - M) * (max_val - K) / max_val;
+	unsigned b = (max_val - Y) * (max_val - K) / max_val;
+
+	// clamp values to [0..max_val]
+	T red	= (T)CLAMP(r, (unsigned)0, max_val);
+	T green	= (T)CLAMP(g, (unsigned)0, max_val);
+	T blue	= (T)CLAMP(b, (unsigned)0, max_val);
+
+	assignRGB(red, green, blue, out);
+}
+
+template <class T>
+static void 
+_convertCMYKtoRGBA(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
+	const BOOL hasBlack = (samplesperpixel > 3) ? TRUE : FALSE;
+	const T MAX_VAL = std::numeric_limits<T>::max();
+		
+	T K = 0;
+	for(unsigned y = 0; y < height; y++) {
+		T *line = (T*)line_start;
+
+		for(unsigned x = 0; x < width; x++) {
+			if(hasBlack) {
+				K = line[FI_RGBA_ALPHA];			
+				line[FI_RGBA_ALPHA] = MAX_VAL; // TODO write the first extra channel as alpha!
+			}			
+			
+			CMYKToRGB<T>(line[0], line[1], line[2], K, line);
+			
+			line += samplesperpixel;
+		}
+		line_start += pitch;
+	}
+}
+
+BOOL 
+ConvertCMYKtoRGBA(FIBITMAP* dib) {
+	if(!FreeImage_HasPixels(dib)) {
+		return FALSE;
+	}
+		
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	const unsigned bytesperpixel = FreeImage_GetBPP(dib)/8;
+	
+	unsigned channelSize = 1;
+	if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
+		channelSize = sizeof(WORD);
+	} else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
+		return FALSE;
+	}
+				
+	const unsigned width = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+	const unsigned pitch = FreeImage_GetPitch(dib);
+	
+	unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
+
+	if(channelSize == sizeof(WORD)) {
+		_convertCMYKtoRGBA<WORD>(width, height, line_start, pitch, samplesperpixel);
+	} else {
+		_convertCMYKtoRGBA<BYTE>(width, height, line_start, pitch, samplesperpixel);
+	}
+
+	return TRUE;	
+}
+
+// ----------------------------------------------------------
+
+/**
+CIELab -> XYZ conversion from http://www.easyrgb.com/
+*/
+static void 
+CIELabToXYZ(float L, float a, float b, float *X, float *Y, float *Z) {
+	float pow_3;
+	
+	// CIELab -> XYZ conversion 
+	// ------------------------
+	float var_Y = (L + 16.F ) / 116.F;
+	float var_X = a / 500.F + var_Y;
+	float var_Z = var_Y - b / 200.F;
+
+	pow_3 = powf(var_Y, 3);
+	if(pow_3 > 0.008856F) {
+		var_Y = pow_3;
+	} else {
+		var_Y = ( var_Y - 16.F / 116.F ) / 7.787F;
+	}
+	pow_3 = powf(var_X, 3);
+	if(pow_3 > 0.008856F) {
+		var_X = pow_3;
+	} else {
+		var_X = ( var_X - 16.F / 116.F ) / 7.787F;
+	}
+	pow_3 = powf(var_Z, 3);
+	if(pow_3 > 0.008856F) {
+		var_Z = pow_3;
+	} else {
+		var_Z = ( var_Z - 16.F / 116.F ) / 7.787F;
+	}
+
+	static const float ref_X =  95.047F;
+	static const float ref_Y = 100.000F;
+	static const float ref_Z = 108.883F;
+
+	*X = ref_X * var_X;	// ref_X = 95.047 (Observer= 2°, Illuminant= D65)
+	*Y = ref_Y * var_Y;	// ref_Y = 100.000
+	*Z = ref_Z * var_Z;	// ref_Z = 108.883
+}
+
+/**
+XYZ -> RGB conversion from http://www.easyrgb.com/
+*/
+static void 
+XYZToRGB(float X, float Y, float Z, float *R, float *G, float *B) {
+	float var_X = X / 100; // X from 0 to  95.047 (Observer = 2°, Illuminant = D65)
+	float var_Y = Y / 100; // Y from 0 to 100.000
+	float var_Z = Z / 100; // Z from 0 to 108.883
+
+	float var_R = var_X *  3.2406F + var_Y * -1.5372F + var_Z * -0.4986F;
+	float var_G = var_X * -0.9689F + var_Y *  1.8758F + var_Z *  0.0415F;
+	float var_B = var_X *  0.0557F + var_Y * -0.2040F + var_Z *  1.0570F;
+
+	float exponent = 1.F / 2.4F;
+
+	if(var_R > 0.0031308F) {
+		var_R = 1.055F * powf(var_R, exponent) - 0.055F;
+	} else {
+		var_R = 12.92F * var_R;
+	}
+	if(var_G > 0.0031308F) {
+		var_G = 1.055F * powf(var_G, exponent) - 0.055F;
+	} else {
+		var_G = 12.92F * var_G;
+	}
+	if(var_B > 0.0031308F) {
+		var_B = 1.055F * powf(var_B, exponent) - 0.055F;
+	} else {
+		var_B = 12.92F * var_B;
+	}
+
+	*R = var_R;
+	*G = var_G;
+	*B = var_B;
+}
+
+template<class T>
+static void 
+CIELabToRGB(float L, float a, float b, T *rgb) {
+	float X, Y, Z;
+	float R, G, B;
+	const float max_val = std::numeric_limits<T>::max();
+
+	CIELabToXYZ(L, a, b, &X, &Y, &Z);
+	XYZToRGB(X, Y, Z, &R, &G, &B);
+	
+	// clamp values to [0..max_val]
+	T red	= (T)CLAMP(R * max_val, 0.0F, max_val);
+	T green	= (T)CLAMP(G * max_val, 0.0F, max_val);
+	T blue	= (T)CLAMP(B * max_val, 0.0F, max_val);
+
+	assignRGB(red, green, blue, rgb);
+}
+
+template<class T>
+static void 
+_convertLABtoRGB(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
+	const unsigned max_val = std::numeric_limits<T>::max();
+	const float sL = 100.F / max_val;
+	const float sa = 256.F / max_val;
+	const float sb = 256.F / max_val;
+	
+	for(unsigned y = 0; y < height; y++) {
+		T *line = (T*)line_start;
+
+		for(unsigned x = 0; x < width; x++) {
+			CIELabToRGB(line[0]* sL, line[1]* sa - 128.F, line[2]* sb - 128.F, line);
+			
+			line += samplesperpixel;
+		}
+		line_start += pitch;
+	}
+}
+
+BOOL
+ConvertLABtoRGB(FIBITMAP* dib) {
+	if(!FreeImage_HasPixels(dib)) {
+		return FALSE;
+	}
+		
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
+	
+	unsigned channelSize = 1;
+	if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
+		channelSize = sizeof(WORD);
+	} else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
+		return FALSE;
+	}
+				
+	const unsigned width = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+	BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+	const unsigned pitch = FreeImage_GetPitch(dib);
+	
+	unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
+			
+	if(channelSize == 1) {
+		_convertLABtoRGB<BYTE>(width, height, line_start, pitch, samplesperpixel);
+	}
+	else {
+		_convertLABtoRGB<WORD>(width, height, line_start, pitch, samplesperpixel);
+	}
+
+	return TRUE;	
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP* 
+RemoveAlphaChannel(FIBITMAP* src) { 
+
+	if(!FreeImage_HasPixels(src)) {
+		return NULL;
+	}
+
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+		
+	switch(image_type) {
+		case FIT_BITMAP:
+			if(FreeImage_GetBPP(src) == 32) {
+				// convert to 24-bit
+				return FreeImage_ConvertTo24Bits(src);
+			}
+			break;
+		case FIT_RGBA16:
+			// convert to RGB16
+			return FreeImage_ConvertToRGB16(src);
+		case FIT_RGBAF:
+			// convert to RGBF
+			return FreeImage_ConvertToRGBF(src);
+		default:
+			// unsupported image type
+			return NULL;
+	}
+
+	return NULL;
+}
+
+
+// ==========================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize) {
+	return FreeImage_ColorQuantizeEx(dib, quantize);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
+	if( PaletteSize < 2 ) PaletteSize = 2;
+	if( PaletteSize > 256 ) PaletteSize = 256;
+	if( ReserveSize < 0 ) ReserveSize = 0;
+	if( ReserveSize > PaletteSize ) ReserveSize = PaletteSize;
+	if (FreeImage_HasPixels(dib)) {
+		const unsigned bpp = FreeImage_GetBPP(dib);
+		if((FreeImage_GetImageType(dib) == FIT_BITMAP) && (bpp == 24 || bpp == 32)) {
+			switch(quantize) {
+				case FIQ_WUQUANT :
+				{
+					try {
+						WuQuantizer Q (dib);
+						FIBITMAP *dst = Q.Quantize(PaletteSize, ReserveSize, ReservePalette);
+						if(dst) {
+							// copy metadata from src to dst
+							FreeImage_CloneMetadata(dst, dib);
+						}
+						return dst;
+					} catch (const char *) {
+						return NULL;
+					}
+					break;
+				}
+				case FIQ_NNQUANT :
+				{
+					if (bpp == 32) {
+						// 32-bit images not supported by NNQUANT
+						return NULL;
+					}
+					// sampling factor in range 1..30. 
+					// 1 => slower (but better), 30 => faster. Default value is 1
+					const int sampling = 1;
+
+					NNQuantizer Q(PaletteSize);
+					FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette, sampling);
+					if(dst) {
+						// copy metadata from src to dst
+						FreeImage_CloneMetadata(dst, dib);
+					}
+					return dst;
+				}
+				case FIQ_LFPQUANT :
+				{
+					LFPQuantizer Q(PaletteSize);
+					FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette);
+					if(dst) {
+						// copy metadata from src to dst
+						FreeImage_CloneMetadata(dst, dib);
+					}
+					return dst;
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
+
+// ==========================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+	FIBITMAP *dib = NULL;
+
+	if(copySource) {
+		// allocate a FIBITMAP with internally managed pixel buffer
+		dib = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
+		if(!dib) {
+			return NULL;
+		}
+		// copy user provided pixel buffer into the dib
+		const unsigned linesize = FreeImage_GetLine(dib);
+		for(int y = 0; y < height; y++) {
+			memcpy(FreeImage_GetScanLine(dib, y), bits, linesize);
+			// next line in user's buffer
+			bits += pitch;
+		}
+		// flip pixels vertically if needed
+		if(topdown) {
+			FreeImage_FlipVertical(dib);
+		}
+	}
+	else {
+		// allocate a FIBITMAP using a wrapper to user provided pixel buffer
+		dib = FreeImage_AllocateHeaderForBits(bits, pitch, type, width, height, bpp, red_mask, green_mask, blue_mask);
+		if(!dib) {
+			return NULL;
+		}
+		// flip pixels vertically if needed
+		if(topdown) {
+			FreeImage_FlipVertical(dib);
+		}
+	}
+
+	return dib;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+	return FreeImage_ConvertFromRawBitsEx(TRUE /* copySource */, bits, FIT_BITMAP, width, height, pitch, bpp, red_mask, green_mask, blue_mask, topdown);
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+	if (FreeImage_HasPixels(dib) && (bits != NULL)) {
+		for (unsigned i = 0; i < FreeImage_GetHeight(dib); ++i) {
+			BYTE *scanline = FreeImage_GetScanLine(dib, topdown ? (FreeImage_GetHeight(dib) - i - 1) : i);
+
+			if ((bpp == 16) && (FreeImage_GetBPP(dib) == 16)) {
+				// convert 555 to 565 or vice versa
+
+				if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) {
+					if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+						FreeImage_ConvertLine16_565_To16_555(bits, scanline, FreeImage_GetWidth(dib));
+					} else {
+						memcpy(bits, scanline, FreeImage_GetLine(dib));
+					}
+				} else {
+					if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) {
+						FreeImage_ConvertLine16_555_To16_565(bits, scanline, FreeImage_GetWidth(dib));
+					} else {
+						memcpy(bits, scanline, FreeImage_GetLine(dib));
+					}
+				}
+			} else if (FreeImage_GetBPP(dib) != bpp) {
+				switch(FreeImage_GetBPP(dib)) {
+					case 1 :
+						switch(bpp) {
+							CONVERT(1, 8)
+							CONVERTTO16WITHPALETTE(1)
+							CONVERTWITHPALETTE(1, 24)
+							CONVERTWITHPALETTE(1, 32)
+						}
+
+						break;
+
+					case 4 :
+						switch(bpp) {
+							CONVERT(4, 8)
+							CONVERTTO16WITHPALETTE(4)
+							CONVERTWITHPALETTE(4, 24)
+							CONVERTWITHPALETTE(4, 32)
+						}
+
+						break;
+
+					case 8 :
+						switch(bpp) {
+							CONVERTTO16WITHPALETTE(8)
+							CONVERTWITHPALETTE(8, 24)
+							CONVERTWITHPALETTE(8, 32)
+						}
+
+						break;
+
+					case 24 :
+						switch(bpp) {
+							CONVERT(24, 8)
+							CONVERTTO16(24)
+							CONVERT(24, 32)
+						}
+
+						break;
+
+					case 32 :
+						switch(bpp) {
+							CONVERT(32, 8)
+							CONVERTTO16(32)
+							CONVERT(32, 24)
+						}
+
+						break;
+				}
+			} else {
+				memcpy(bits, scanline, FreeImage_GetLine(dib));
+			}
+
+			bits += pitch;
+		}
+	}
+}
diff --git a/files/Source/FreeImage/Conversion16_555.cpp b/files/Source/FreeImage/Conversion16_555.cpp
new file mode 100644
index 0000000..abaf2f1
--- /dev/null
+++ b/files/Source/FreeImage/Conversion16_555.cpp
@@ -0,0 +1,209 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+#define RGB555(b, g, r) ((((b) >> 3) << FI16_555_BLUE_SHIFT) | (((g) >> 3) << FI16_555_GREEN_SHIFT) | (((r) >> 3) << FI16_555_RED_SHIFT))
+
+// ----------------------------------------------------------
+//  internal conversions X to 16 bits (555)
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		int index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+		new_bits[cols] = RGB555(palette[index].rgbBlue, palette[index].rgbGreen, palette[index].rgbRed);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+	BOOL lonibble = FALSE;
+	int x = 0;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		RGBQUAD *grab_palette;
+
+		if (lonibble) {
+			grab_palette = palette + LOWNIBBLE(source[x++]);
+		} else {
+			grab_palette = palette + (HINIBBLE(source[x]) >> 4);								
+		}
+
+		new_bits[cols] = RGB555(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+
+		lonibble = !lonibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		RGBQUAD *grab_palette = palette + source[cols];
+
+		new_bits[cols] = RGB555(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16_565_To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *src_bits = (WORD *)source;
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB555((((src_bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F,
+			                    (((src_bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+								(((src_bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB555(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+		source += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB555(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+		source += 4;
+	}
+}
+
+// ----------------------------------------------------------
+//   smart convert X to 16 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo16Bits555(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) return NULL;
+
+	const int width = FreeImage_GetWidth(dib);
+	const int height = FreeImage_GetHeight(dib);
+	const int bpp = FreeImage_GetBPP(dib);
+
+	if(bpp == 16) {
+		if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+			// RGB 565
+			FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+			if(new_dib == NULL) {
+				return NULL;
+			}
+			for (int rows = 0; rows < height; rows++) {
+				FreeImage_ConvertLine16_565_To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+			}
+
+			// copy metadata from src to dst
+			FreeImage_CloneMetadata(new_dib, dib);
+
+			return new_dib;
+		} else {
+			// RGB 555
+			return FreeImage_Clone(dib);
+		}
+	}
+	else {
+		// other bpp cases => convert to RGB 555
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		switch (bpp) {
+			case 1 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine1To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 4 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine4To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 8 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine8To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 24 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine24To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+
+				return new_dib;
+			}
+
+			case 32 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine32To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+
+				return new_dib;
+			}
+
+			default :
+				// unreachable code ...
+				FreeImage_Unload(new_dib);
+				break;
+
+		}
+	}
+
+	return NULL;
+}
diff --git a/files/Source/FreeImage/Conversion16_565.cpp b/files/Source/FreeImage/Conversion16_565.cpp
new file mode 100644
index 0000000..eb3dd9d
--- /dev/null
+++ b/files/Source/FreeImage/Conversion16_565.cpp
@@ -0,0 +1,204 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//  internal conversions X to 16 bits (565)
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		int index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+		new_bits[cols] = RGB565(palette[index].rgbBlue, palette[index].rgbGreen, palette[index].rgbRed);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+	BOOL lonibble = FALSE;
+	int x = 0;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		RGBQUAD *grab_palette;
+
+		if (lonibble) {
+			grab_palette = palette + LOWNIBBLE(source[x++]);
+		} else {
+			grab_palette = palette + (HINIBBLE(source[x]) >> 4);								
+		}
+
+		new_bits[cols] = RGB565(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+
+		lonibble = !lonibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		RGBQUAD *grab_palette = palette + source[cols];
+
+		new_bits[cols] = RGB565(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16_555_To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *src_bits = (WORD *)source;
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB565((((src_bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F,
+			                    (((src_bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+								(((src_bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB565(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+		source += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *new_bits = (WORD *)target;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		new_bits[cols] = RGB565(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+		source += 4;
+	}
+}
+
+// ----------------------------------------------------------
+//   smart convert X to 16 bits (565)
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo16Bits565(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) return NULL;
+
+	const int width = FreeImage_GetWidth(dib);
+	const int height = FreeImage_GetHeight(dib);
+	const int bpp = FreeImage_GetBPP(dib);
+
+	if(bpp == 16) {
+		if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) {
+			// RGB 555
+			FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+			if(new_dib == NULL) {
+				return NULL;
+			}
+			for (int rows = 0; rows < height; rows++) {
+				FreeImage_ConvertLine16_555_To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+			}
+
+			// copy metadata from src to dst
+			FreeImage_CloneMetadata(new_dib, dib);
+
+			return new_dib;
+		} else {
+			// RGB 565
+			return FreeImage_Clone(dib);
+		}
+	}
+	else {
+		// other bpp cases => convert to RGB 565
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		switch (bpp) {
+			case 1 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine1To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 4 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine4To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 8 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine8To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+
+				return new_dib;
+			}
+
+			case 24 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine24To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+
+				return new_dib;
+			}
+
+			case 32 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine32To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+
+				return new_dib;
+			}
+
+			default :
+				// unreachable code ...
+				FreeImage_Unload(new_dib);
+				break;
+		}
+	}
+
+	return NULL;
+}
diff --git a/files/Source/FreeImage/Conversion24.cpp b/files/Source/FreeImage/Conversion24.cpp
new file mode 100644
index 0000000..3b7a800
--- /dev/null
+++ b/files/Source/FreeImage/Conversion24.cpp
@@ -0,0 +1,252 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Dale Larson (dlarson@norsesoft.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//  internal conversions X to 24 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		BYTE index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+		target[FI_RGBA_BLUE] = palette[index].rgbBlue;
+		target[FI_RGBA_GREEN] = palette[index].rgbGreen;
+		target[FI_RGBA_RED] = palette[index].rgbRed;
+
+		target += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	BOOL low_nibble = FALSE;
+	int x = 0;
+
+	for (int cols = 0; cols < width_in_pixels; ++cols ) {
+		if (low_nibble) {
+			target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
+			target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
+			target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
+
+			x++;
+		} else {
+			target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+			target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+			target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
+		}
+
+		low_nibble = !low_nibble;
+
+		target += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
+		target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
+		target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
+
+		target += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_RED]   = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_BLUE]  = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+
+		target += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_RED]   = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+		target[FI_RGBA_BLUE]  = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+
+		target += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE];
+		target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN];
+		target[FI_RGBA_RED] = source[FI_RGBA_RED];
+
+		target += 3;
+		source += 4;
+	}
+}
+
+// ----------------------------------------------------------
+//   smart convert X to 24 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo24Bits(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const unsigned bpp = FreeImage_GetBPP(dib);
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+	if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) {
+		return NULL;
+	}
+	
+	const int width = FreeImage_GetWidth(dib);
+	const int height = FreeImage_GetHeight(dib);
+
+	if(image_type == FIT_BITMAP) {
+		if(bpp == 24) {
+			return FreeImage_Clone(dib);
+		}
+
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		switch(bpp) {
+			case 1 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine1To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));					
+				}
+				return new_dib;
+			}
+
+			case 4 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine4To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+				return new_dib;
+			}
+				
+			case 8 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine8To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+				return new_dib;
+			}
+
+			case 16 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+						FreeImage_ConvertLine16To24_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					} else {
+						// includes case where all the masks are 0
+						FreeImage_ConvertLine16To24_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					}
+				}
+				return new_dib;
+			}
+
+			case 32 :
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine32To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+				return new_dib;
+			}
+		}
+	
+	} else if(image_type == FIT_RGB16) {
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		const unsigned src_pitch = FreeImage_GetPitch(dib);
+		const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+		const BYTE *src_bits = FreeImage_GetBits(dib);
+		BYTE *dst_bits = FreeImage_GetBits(new_dib);
+		for (int rows = 0; rows < height; rows++) {
+			const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+			RGBTRIPLE *dst_pixel = (RGBTRIPLE*)dst_bits;
+			for(int cols = 0; cols < width; cols++) {
+				dst_pixel[cols].rgbtRed   = (BYTE)(src_pixel[cols].red   >> 8);
+				dst_pixel[cols].rgbtGreen = (BYTE)(src_pixel[cols].green >> 8);
+				dst_pixel[cols].rgbtBlue  = (BYTE)(src_pixel[cols].blue  >> 8);
+			}
+			src_bits += src_pitch;
+			dst_bits += dst_pitch;
+		}
+
+		return new_dib;
+
+	} else if(image_type == FIT_RGBA16) {
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		const unsigned src_pitch = FreeImage_GetPitch(dib);
+		const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+		const BYTE *src_bits = FreeImage_GetBits(dib);
+		BYTE *dst_bits = FreeImage_GetBits(new_dib);
+		for (int rows = 0; rows < height; rows++) {
+			const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+			RGBTRIPLE *dst_pixel = (RGBTRIPLE*)dst_bits;
+			for(int cols = 0; cols < width; cols++) {
+				dst_pixel[cols].rgbtRed   = (BYTE)(src_pixel[cols].red   >> 8);
+				dst_pixel[cols].rgbtGreen = (BYTE)(src_pixel[cols].green >> 8);
+				dst_pixel[cols].rgbtBlue  = (BYTE)(src_pixel[cols].blue  >> 8);
+			}
+			src_bits += src_pitch;
+			dst_bits += dst_pitch;
+		}		
+
+		return new_dib;
+	}
+
+	return NULL;
+}
diff --git a/files/Source/FreeImage/Conversion32.cpp b/files/Source/FreeImage/Conversion32.cpp
new file mode 100644
index 0000000..4874dcf
--- /dev/null
+++ b/files/Source/FreeImage/Conversion32.cpp
@@ -0,0 +1,345 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//  internal conversions X to 32 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+		target[FI_RGBA_BLUE]	= palette[index].rgbBlue;
+		target[FI_RGBA_GREEN]	= palette[index].rgbGreen;
+		target[FI_RGBA_RED]		= palette[index].rgbRed;
+		target[FI_RGBA_ALPHA]	= 0xFF;
+		target += 4;
+	}	
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	BOOL low_nibble = FALSE;
+	int x = 0;
+
+	for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
+		if (low_nibble) {
+			target[FI_RGBA_BLUE]	= palette[LOWNIBBLE(source[x])].rgbBlue;
+			target[FI_RGBA_GREEN]	= palette[LOWNIBBLE(source[x])].rgbGreen;
+			target[FI_RGBA_RED]		= palette[LOWNIBBLE(source[x])].rgbRed;
+
+			x++;
+		} else {
+			target[FI_RGBA_BLUE]	= palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+			target[FI_RGBA_GREEN]	= palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+			target[FI_RGBA_RED]		= palette[HINIBBLE(source[x]) >> 4].rgbRed;
+		}
+
+		low_nibble = !low_nibble;
+
+		target[FI_RGBA_ALPHA] = 0xFF;
+		target += 4;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_BLUE]	= palette[source[cols]].rgbBlue;
+		target[FI_RGBA_GREEN]	= palette[source[cols]].rgbGreen;
+		target[FI_RGBA_RED]		= palette[source[cols]].rgbRed;
+		target[FI_RGBA_ALPHA]	= 0xFF;
+		target += 4;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_RED]   = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_BLUE]  = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_ALPHA] = 0xFF;
+		target += 4;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_RED]   = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+		target[FI_RGBA_BLUE]  = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+		target[FI_RGBA_ALPHA] = 0xFF;
+		target += 4;
+	}
+}
+/*
+void DLL_CALLCONV
+FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		*(DWORD *)target = (*(DWORD *) source & FI_RGBA_RGB_MASK) | FI_RGBA_ALPHA_MASK;
+		target += 4;
+		source += 3;
+	}
+}
+*/
+/**
+This unoptimized version of the conversion function avoid an undetermined bug with VC++ SP6. 
+The bug occurs in release mode only, when the image height is equal to 537 
+(try e.g. a size of 432x537 to reproduce the bug with the optimized function).
+*/
+void DLL_CALLCONV
+FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_RED]   = source[FI_RGBA_RED];
+		target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN];
+		target[FI_RGBA_BLUE]  = source[FI_RGBA_BLUE];
+		target[FI_RGBA_ALPHA] = 0xFF;
+		target += 4;
+		source += 3;
+	}
+}
+
+// ----------------------------------------------------------
+
+inline void 
+FreeImage_ConvertLine1To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+		target[FI_RGBA_BLUE]	= palette[index].rgbBlue;
+		target[FI_RGBA_GREEN]	= palette[index].rgbGreen;
+		target[FI_RGBA_RED]		= palette[index].rgbRed;
+		target[FI_RGBA_ALPHA] = (index < transparent_pixels) ? table[index] : 255;		
+		target += 4;
+	}	
+}
+
+inline void 
+FreeImage_ConvertLine4To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+	BOOL low_nibble = FALSE;
+	int x = 0;
+
+	for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
+		if (low_nibble) {
+			target[FI_RGBA_BLUE]	= palette[LOWNIBBLE(source[x])].rgbBlue;
+			target[FI_RGBA_GREEN]	= palette[LOWNIBBLE(source[x])].rgbGreen;
+			target[FI_RGBA_RED]		= palette[LOWNIBBLE(source[x])].rgbRed;
+			target[FI_RGBA_ALPHA]	= (LOWNIBBLE(source[x]) < transparent_pixels) ? table[LOWNIBBLE(source[x])] : 255;
+
+			x++;
+		} else {
+			target[FI_RGBA_BLUE]	= palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+			target[FI_RGBA_GREEN]	= palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+			target[FI_RGBA_RED]		= palette[HINIBBLE(source[x]) >> 4].rgbRed;
+			target[FI_RGBA_ALPHA]	= (HINIBBLE(source[x] >> 4) < transparent_pixels) ? table[HINIBBLE(source[x]) >> 4] : 255;
+		}
+
+		low_nibble = !low_nibble;
+				
+		target += 4;
+	}
+}
+
+inline void 
+FreeImage_ConvertLine8To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		target[FI_RGBA_BLUE]	= palette[source[cols]].rgbBlue;
+		target[FI_RGBA_GREEN]	= palette[source[cols]].rgbGreen;
+		target[FI_RGBA_RED]		= palette[source[cols]].rgbRed;
+		target[FI_RGBA_ALPHA] = (source[cols] < transparent_pixels) ? table[source[cols]] : 255;
+		target += 4;		
+	}
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo32Bits(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const int bpp = FreeImage_GetBPP(dib);
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	
+	if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) {
+		return NULL;
+	}
+	
+	const int width = FreeImage_GetWidth(dib);
+	const int height = FreeImage_GetHeight(dib);
+
+	if(image_type == FIT_BITMAP) {
+
+		if(bpp == 32) {
+			return FreeImage_Clone(dib);
+		}
+
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		BOOL bIsTransparent = FreeImage_IsTransparent(dib);
+
+		switch(bpp) {
+			case 1:
+			{
+				if(bIsTransparent) {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine1To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+					}
+				} else {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine1To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+					}					
+				}
+
+				return new_dib;
+			}
+
+			case 4:
+			{
+				if(bIsTransparent) {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine4To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+					}
+				} else {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine4To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+					}					
+				}
+
+				return new_dib;
+			}
+				
+			case 8:
+			{
+				if(bIsTransparent) {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine8To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+					}
+				} else {
+					for (int rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine8To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+					}					
+				}
+
+				return new_dib;
+			}
+
+			case 16:
+			{
+				for (int rows = 0; rows < height; rows++) {
+					if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+						FreeImage_ConvertLine16To32_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					} else {
+						// includes case where all the masks are 0
+						FreeImage_ConvertLine16To32_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					}
+				}
+
+				return new_dib;
+			}
+
+			case 24:
+			{
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine24To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+
+				return new_dib;
+			}
+		}
+
+	} else if(image_type == FIT_RGB16) {
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		const unsigned src_pitch = FreeImage_GetPitch(dib);
+		const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+		const BYTE *src_bits = FreeImage_GetBits(dib);
+		BYTE *dst_bits = FreeImage_GetBits(new_dib);
+		for (int rows = 0; rows < height; rows++) {
+			const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+			RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
+			for(int cols = 0; cols < width; cols++) {
+				dst_pixel[cols].rgbRed		= (BYTE)(src_pixel[cols].red   >> 8);
+				dst_pixel[cols].rgbGreen	= (BYTE)(src_pixel[cols].green >> 8);
+				dst_pixel[cols].rgbBlue		= (BYTE)(src_pixel[cols].blue  >> 8);
+				dst_pixel[cols].rgbReserved = (BYTE)0xFF;
+			}
+			src_bits += src_pitch;
+			dst_bits += dst_pitch;
+		}
+
+		return new_dib;
+
+	} else if(image_type == FIT_RGBA16) {
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		const unsigned src_pitch = FreeImage_GetPitch(dib);
+		const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+		const BYTE *src_bits = FreeImage_GetBits(dib);
+		BYTE *dst_bits = FreeImage_GetBits(new_dib);
+		for (int rows = 0; rows < height; rows++) {
+			const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+			RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
+			for(int cols = 0; cols < width; cols++) {
+				dst_pixel[cols].rgbRed		= (BYTE)(src_pixel[cols].red   >> 8);
+				dst_pixel[cols].rgbGreen	= (BYTE)(src_pixel[cols].green >> 8);
+				dst_pixel[cols].rgbBlue		= (BYTE)(src_pixel[cols].blue  >> 8);
+				dst_pixel[cols].rgbReserved = (BYTE)(src_pixel[cols].alpha >> 8);
+			}
+			src_bits += src_pitch;
+			dst_bits += dst_pitch;
+		}		
+
+		return new_dib;
+	}
+	
+	return NULL;
+}
diff --git a/files/Source/FreeImage/Conversion4.cpp b/files/Source/FreeImage/Conversion4.cpp
new file mode 100644
index 0000000..13048b6
--- /dev/null
+++ b/files/Source/FreeImage/Conversion4.cpp
@@ -0,0 +1,246 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Riley McNiff (rmcniff@marexgroup.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//  internal conversions X to 4 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To4(BYTE *target, BYTE *source, int width_in_pixels) {
+	BOOL hinibble = TRUE;
+	for (int cols = 0; cols < width_in_pixels; cols++){
+		if (hinibble == TRUE){
+			target[cols >> 1] = ((source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 15 : 0) << 4;
+		} 
+		else {
+			target[cols >> 1] |= ((source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 15 : 0);
+		}
+
+		hinibble = !hinibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To4(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+	BOOL hinibble = TRUE;
+	BYTE index;
+
+	for (int cols = 0; cols < width_in_pixels; cols++){
+		index = GREY(palette[source[cols]].rgbRed, palette[source[cols]].rgbGreen, palette[source[cols]].rgbBlue);
+		if (hinibble) {
+			target[cols >> 1] = (index & 0xF0);
+		} else {
+			target[cols >> 1] |= (index >> 4);
+		}
+
+		hinibble = !hinibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To4_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+	BOOL hinibble = TRUE;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		if (hinibble) {
+			target[cols >> 1] = GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+								(((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+								(((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F)
+								& 0xF0;
+		} else {
+			target[cols >> 1] |= GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+								(((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+								(((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F)
+								>> 4;
+		}
+		
+		hinibble = !hinibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To4_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	WORD *bits = (WORD *)source;
+	BOOL hinibble = TRUE;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		if (hinibble) {
+			target[cols >> 1] = GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+				        (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+						(((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F)
+						& 0xF0;
+		} else {
+			target[cols >> 1] |= GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+				        (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+						(((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F)
+						>> 4;
+		}
+
+		hinibble = !hinibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To4(BYTE *target, BYTE *source, int width_in_pixels) {
+	BOOL hinibble = TRUE;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		if (hinibble) {
+			target[cols >> 1] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) & 0xF0;
+		} else {
+			target[cols >> 1] |= GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) >> 4;
+		}
+
+		source += 3;
+		hinibble = !hinibble;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To4(BYTE *target, BYTE *source, int width_in_pixels) {
+	BOOL hinibble = TRUE;
+
+	for (int cols = 0; cols < width_in_pixels; cols++) {
+		if (hinibble) {
+			target[cols >> 1] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) & 0xF0;
+		} else {
+			target[cols >> 1] |= GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) >> 4;
+		}
+
+		source += 4;
+		hinibble = !hinibble;
+	}
+}
+
+// ----------------------------------------------------------
+//   smart convert X to 4 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo4Bits(FIBITMAP *dib) {
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const int bpp = FreeImage_GetBPP(dib);
+
+	if(bpp != 4) {
+		const int width  = FreeImage_GetWidth(dib);
+		const int height = FreeImage_GetHeight(dib);
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 4);
+
+		if(new_dib == NULL) {
+			return NULL;
+		}
+
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		// Build a greyscale palette (*always* needed for image processing)
+
+		RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+		for(int i = 0; i < 16; i++) {
+			new_pal[i].rgbRed	= (BYTE)((i << 4) + i);
+			new_pal[i].rgbGreen = (BYTE)((i << 4) + i);
+			new_pal[i].rgbBlue	= (BYTE)((i << 4) + i);
+		}
+
+		switch(bpp) {
+			case 1:
+			{
+				if(FreeImage_GetColorType(dib) == FIC_PALETTE) {
+
+					// Copy the palette
+
+					RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+					memcpy(&new_pal[0], &old_pal[0], sizeof(RGBQUAD));
+					memcpy(&new_pal[15], &old_pal[1], sizeof(RGBQUAD));
+
+				}
+				else if(FreeImage_GetColorType(dib) == FIC_MINISWHITE) {
+					
+					// Reverse the grayscale palette
+
+					for(int i = 0; i < 16; i++) {
+						new_pal[i].rgbRed = new_pal[i].rgbGreen = new_pal[i].rgbBlue = (BYTE)(255 - ((i << 4) + i));
+					}
+				}
+
+				// Expand and copy the bitmap data
+
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine1To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+				return new_dib;
+			}
+
+			case 8 :
+			{
+				// Expand and copy the bitmap data
+
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine8To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+				}
+				return new_dib;
+			}
+
+			case 16 :
+			{
+				// Expand and copy the bitmap data
+
+				for (int rows = 0; rows < height; rows++) {
+					if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+						FreeImage_ConvertLine16To4_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					} else {
+						FreeImage_ConvertLine16To4_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					}
+				}
+				
+				return new_dib;
+			}
+
+			case 24 :
+			{
+				// Expand and copy the bitmap data
+
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine24To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);					
+				}
+				return new_dib;
+			}
+
+			case 32 :
+			{
+				// Expand and copy the bitmap data
+
+				for (int rows = 0; rows < height; rows++) {
+					FreeImage_ConvertLine32To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+				}
+				return new_dib;
+			}
+		}
+	}
+
+	return FreeImage_Clone(dib);
+}
diff --git a/files/Source/FreeImage/Conversion8.cpp b/files/Source/FreeImage/Conversion8.cpp
new file mode 100644
index 0000000..c4f9b22
--- /dev/null
+++ b/files/Source/FreeImage/Conversion8.cpp
@@ -0,0 +1,305 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//  internal conversions X to 8 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (unsigned cols = 0; cols < (unsigned)width_in_pixels; cols++)
+		target[cols] = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 255 : 0;	
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To8(BYTE *target, BYTE *source, int width_in_pixels) {
+	unsigned count_new = 0;
+	unsigned count_org = 0;
+	BOOL hinibble = TRUE;
+
+	while (count_new < (unsigned)width_in_pixels) {
+		if (hinibble) {
+			target[count_new] = (source[count_org] >> 4);
+		} else {
+			target[count_new] = (source[count_org] & 0x0F);
+			count_org++;
+		}
+		hinibble = !hinibble;
+		count_new++;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To8_555(BYTE *target, BYTE *source, int width_in_pixels) {
+	const WORD *const bits = (WORD *)source;
+	for (unsigned cols = 0; cols < (unsigned)width_in_pixels; cols++) {
+		target[cols] = GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+			                (((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+							(((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To8_565(BYTE *target, BYTE *source, int width_in_pixels) {
+	const WORD *const bits = (WORD *)source;
+	for (unsigned cols = 0; cols < (unsigned)width_in_pixels; cols++) {
+		target[cols] = GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+			        (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+					(((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To8(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (unsigned cols = 0; cols < (unsigned)width_in_pixels; cols++) {
+		target[cols] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]);
+		source += 3;
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To8(BYTE *target, BYTE *source, int width_in_pixels) {
+	for (unsigned cols = 0; cols < (unsigned)width_in_pixels; cols++) {
+		target[cols] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]);
+		source += 4;
+	}
+}
+
+// ----------------------------------------------------------
+//   smart convert X to 8 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo8Bits(FIBITMAP *dib) {
+	if (!FreeImage_HasPixels(dib)) {
+		return NULL;
+	}
+
+	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+	if (image_type != FIT_BITMAP && image_type != FIT_UINT16) {
+		return NULL;
+	}
+
+	const unsigned bpp = FreeImage_GetBPP(dib);
+
+	if (bpp != 8) {
+
+		const unsigned width = FreeImage_GetWidth(dib);
+		const unsigned height = FreeImage_GetHeight(dib);
+
+		// Allocate a destination image
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+		if (new_dib == NULL) {
+			return NULL;
+		}
+
+		// Copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		// Palette of destination image has already been initialized
+		RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+		const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+		if (image_type == FIT_BITMAP) {
+
+			switch(bpp) {
+				case 1:
+				{
+					if (color_type == FIC_PALETTE) {
+						// Copy the palette
+						RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+						new_pal[0] = old_pal[0];
+						new_pal[255] = old_pal[1];
+
+					} else if (color_type == FIC_MINISWHITE) {
+						// Create a reverse grayscale palette
+						CREATE_GREYSCALE_PALETTE_REVERSE(new_pal, 256);
+					}
+
+					// Expand and copy the bitmap data
+					for (unsigned rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine1To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					}
+					return new_dib;
+				}
+
+				case 4 :
+				{
+					if (color_type == FIC_PALETTE) {
+						// Copy the palette
+						memcpy(new_pal, FreeImage_GetPalette(dib), 16 * sizeof(RGBQUAD));
+					}
+
+					// Expand and copy the bitmap data
+					for (unsigned rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine4To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);					
+					}
+					return new_dib;
+				}
+
+				case 16 :
+				{
+					// Expand and copy the bitmap data
+					if (IS_FORMAT_RGB565(dib)) {
+						for (unsigned rows = 0; rows < height; rows++) {
+							FreeImage_ConvertLine16To8_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+						}
+					} else {
+						for (unsigned rows = 0; rows < height; rows++) {
+							FreeImage_ConvertLine16To8_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+						}
+					}
+					return new_dib;
+				}
+
+				case 24 :
+				{
+					// Expand and copy the bitmap data
+					for (unsigned rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine24To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);					
+					}
+					return new_dib;
+				}
+
+				case 32 :
+				{
+					// Expand and copy the bitmap data
+					for (unsigned rows = 0; rows < height; rows++) {
+						FreeImage_ConvertLine32To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+					}
+					return new_dib;
+				}
+			}
+
+		} else if (image_type == FIT_UINT16) {
+
+			const unsigned src_pitch = FreeImage_GetPitch(dib);
+			const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+			const BYTE *src_bits = FreeImage_GetBits(dib);
+			BYTE *dst_bits = FreeImage_GetBits(new_dib);
+
+			for (unsigned rows = 0; rows < height; rows++) {
+				const WORD *const src_pixel = (WORD*)src_bits;
+				BYTE *dst_pixel = (BYTE*)dst_bits;
+				for(unsigned cols = 0; cols < width; cols++) {
+					dst_pixel[cols] = (BYTE)(src_pixel[cols] >> 8);
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+			return new_dib;
+		} 
+
+	} // bpp != 8
+
+	return FreeImage_Clone(dib);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToGreyscale(FIBITMAP *dib) {
+	if (!FreeImage_HasPixels(dib)) {
+		return NULL;
+	}
+
+	const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+	if (color_type == FIC_PALETTE || color_type == FIC_MINISWHITE) {
+
+		const unsigned bpp = FreeImage_GetBPP(dib);
+		const unsigned width  = FreeImage_GetWidth(dib);
+		const unsigned height = FreeImage_GetHeight(dib);
+
+		FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+		if (new_dib == NULL) {
+			return NULL;
+		}
+
+		// Copy metadata from src to dst
+		FreeImage_CloneMetadata(new_dib, dib);
+
+		// Create a greyscale palette
+		BYTE grey_pal[256];
+		const RGBQUAD *pal = FreeImage_GetPalette(dib);
+		const unsigned size = CalculateUsedPaletteEntries(bpp);
+		for (unsigned i = 0; i < size; i++) {
+			grey_pal[i] = GREY(pal->rgbRed, pal->rgbGreen, pal->rgbBlue);
+			pal++;
+		}
+
+		const BYTE *src_bits = FreeImage_GetBits(dib);
+		BYTE *dst_bits = FreeImage_GetBits(new_dib);
+
+		const unsigned src_pitch = FreeImage_GetPitch(dib);
+		const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+
+		switch(bpp) {
+			case 1:
+			{
+				for (unsigned y = 0; y < height; y++) {
+					for (unsigned x = 0; x < width; x++) {
+						const unsigned pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+						dst_bits[x] = grey_pal[pixel];
+					}
+					src_bits += src_pitch;
+					dst_bits += dst_pitch;
+				}
+			}
+			break;
+
+			case 4:
+			{
+				for (unsigned y = 0; y < height; y++) {
+					for (unsigned x = 0; x < width; x++) {
+						const unsigned pixel = x & 0x01 ? src_bits[x >> 1] & 0x0F : src_bits[x >> 1] >> 4;
+						dst_bits[x] = grey_pal[pixel];
+					}
+					src_bits += src_pitch;
+					dst_bits += dst_pitch;
+				}
+			}
+			break;
+
+			case 8:
+			{
+				for (unsigned y = 0; y < height; y++) {
+					for (unsigned x = 0; x < width; x++) {
+						dst_bits[x] = grey_pal[src_bits[x]];
+					}
+					src_bits += src_pitch;
+					dst_bits += dst_pitch;
+				}
+			}
+			break;
+		}
+		return new_dib;
+	} 
+	
+	// Convert the bitmap to 8-bit greyscale
+	return FreeImage_ConvertTo8Bits(dib);
+}
diff --git a/files/Source/FreeImage/ConversionFloat.cpp b/files/Source/FreeImage/ConversionFloat.cpp
new file mode 100644
index 0000000..a36a6d4
--- /dev/null
+++ b/files/Source/FreeImage/ConversionFloat.cpp
@@ -0,0 +1,194 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to Float
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToFloat(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// allow conversion from 8-bit
+			if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) {
+				src = dib;
+			} else {
+				src = FreeImage_ConvertToGreyscale(dib);
+				if(!src) return NULL;
+			}
+			break;
+		}
+		case FIT_UINT16:
+		case FIT_RGB16:
+		case FIT_RGBA16:
+		case FIT_RGBF:
+		case FIT_RGBAF:
+			src = dib;
+			break;
+		case FIT_FLOAT:
+			// float type : clone the src
+			return FreeImage_Clone(dib);
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to float
+
+	const unsigned src_pitch = FreeImage_GetPitch(src);
+	const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+	const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+	BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE *src_pixel = (BYTE*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x] = (float)(src_pixel[x]) / 255;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const WORD *src_pixel = (WORD*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x] = (float)(src_pixel[x]) / 65535;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBF:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBF *src_pixel = (FIRGBF*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert (assume pixel values are in the range [0..1])
+					dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+					dst_pixel[x] = CLAMP(dst_pixel[x], 0.0F, 1.0F);
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBAF:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBAF *src_pixel = (FIRGBAF*)src_bits;
+				float *dst_pixel = (float*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert (assume pixel values are in the range [0..1])
+					dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+					dst_pixel[x] = CLAMP(dst_pixel[x], 0.0F, 1.0F);
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/ConversionRGB16.cpp b/files/Source/FreeImage/ConversionRGB16.cpp
new file mode 100644
index 0000000..9e28205
--- /dev/null
+++ b/files/Source/FreeImage/ConversionRGB16.cpp
@@ -0,0 +1,144 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to RGB16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGB16(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// convert to 24-bit if needed
+			if((FreeImage_GetBPP(dib) == 24) || (FreeImage_GetBPP(dib) == 32)) {
+				src = dib;
+			} else {
+				src = FreeImage_ConvertTo24Bits(dib);
+				if(!src) return NULL;
+			}
+			break;
+		}
+		case FIT_UINT16:
+			// allow conversion from unsigned 16-bit
+			src = dib;
+			break;
+		case FIT_RGB16:
+			// RGB16 type : clone the src
+			return FreeImage_Clone(dib);
+			break;
+		case FIT_RGBA16:
+			// allow conversion from 64-bit RGBA (ignore the alpha channel)
+			src = dib;
+			break;
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_RGB16, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to RGB16
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y);
+				FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					dst_bits[x].red   = src_bits[FI_RGBA_RED] << 8;
+					dst_bits[x].green = src_bits[FI_RGBA_GREEN] << 8;
+					dst_bits[x].blue  = src_bits[FI_RGBA_BLUE] << 8;
+					src_bits += bytespp;
+				}
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
+				FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert by copying greyscale channel to each R, G, B channels
+					dst_bits[x].red   = src_bits[x];
+					dst_bits[x].green = src_bits[x];
+					dst_bits[x].blue  = src_bits[x];
+				}
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y);
+				FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert and skip alpha channel
+					dst_bits[x].red   = src_bits[x].red;
+					dst_bits[x].green = src_bits[x].green;
+					dst_bits[x].blue  = src_bits[x].blue;
+				}
+			}
+		}
+		break;
+
+		default:
+			break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/ConversionRGBA16.cpp b/files/Source/FreeImage/ConversionRGBA16.cpp
new file mode 100644
index 0000000..7924bd9
--- /dev/null
+++ b/files/Source/FreeImage/ConversionRGBA16.cpp
@@ -0,0 +1,147 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to RGBA16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBA16(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// convert to 32-bit if needed
+			if(FreeImage_GetBPP(dib) == 32) {
+				src = dib;
+			} else {
+				src = FreeImage_ConvertTo32Bits(dib);
+				if(!src) return NULL;
+			}
+			break;
+		}
+		case FIT_UINT16:
+			// allow conversion from unsigned 16-bit
+			src = dib;
+			break;
+		case FIT_RGB16:
+			// allow conversion from 48-bit RGB
+			src = dib;
+			break;
+		case FIT_RGBA16:
+			// RGBA16 type : clone the src
+			return FreeImage_Clone(dib);
+			break;
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_RGBA16, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to RGBA16
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// Calculate the number of bytes per pixel (4 for 32-bit)
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y);
+				FIRGBA16 *dst_bits = (FIRGBA16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					dst_bits[x].red		= src_bits[FI_RGBA_RED] << 8;
+					dst_bits[x].green	= src_bits[FI_RGBA_GREEN] << 8;
+					dst_bits[x].blue	= src_bits[FI_RGBA_BLUE] << 8;
+					dst_bits[x].alpha	= src_bits[FI_RGBA_ALPHA] << 8;
+					src_bits += bytespp;
+				}
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
+				FIRGBA16 *dst_bits = (FIRGBA16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert by copying greyscale channel to each R, G, B channels
+					dst_bits[x].red   = src_bits[x];
+					dst_bits[x].green = src_bits[x];
+					dst_bits[x].blue  = src_bits[x];
+					dst_bits[x].alpha = 0xFFFF;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y);
+				FIRGBA16 *dst_bits = (FIRGBA16*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert pixels directly, while adding a "dummy" alpha of 1.0
+					dst_bits[x].red   = src_bits[x].red;
+					dst_bits[x].green = src_bits[x].green;
+					dst_bits[x].blue  = src_bits[x].blue;
+					dst_bits[x].alpha = 0xFFFF;
+				}
+			}
+		}
+		break;
+
+		default:
+			break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/ConversionRGBAF.cpp b/files/Source/FreeImage/ConversionRGBAF.cpp
new file mode 100644
index 0000000..1e23aaa
--- /dev/null
+++ b/files/Source/FreeImage/ConversionRGBAF.cpp
@@ -0,0 +1,250 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Tanner Helland (tannerhelland@users.sf.net)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to RGBAF
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBAF(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// allow conversion from 32-bit
+			const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+			if(color_type != FIC_RGBALPHA) {
+				src = FreeImage_ConvertTo32Bits(dib);
+				if(!src) return NULL;
+			} else {
+				src = dib;
+			}
+			break;
+		}
+		case FIT_UINT16:
+			// allow conversion from 16-bit
+			src = dib;
+			break;
+		case FIT_RGB16:
+			// allow conversion from 48-bit RGB
+			src = dib;
+			break;
+		case FIT_RGBA16:
+			// allow conversion from 64-bit RGBA
+			src = dib;
+			break;
+		case FIT_FLOAT:
+			// allow conversion from 32-bit float
+			src = dib;
+			break;
+		case FIT_RGBF:
+			// allow conversion from 96-bit RGBF
+			src = dib;
+			break;
+		case FIT_RGBAF:
+			// RGBAF type : clone the src
+			return FreeImage_Clone(dib);
+			break;
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_RGBAF, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to RGBAF
+
+	const unsigned src_pitch = FreeImage_GetPitch(src);
+	const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// calculate the number of bytes per pixel (4 for 32-bit)
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE *src_pixel = (BYTE*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel->red   = (float)(src_pixel[FI_RGBA_RED])   / 255.0F;
+					dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F;
+					dst_pixel->blue  = (float)(src_pixel[FI_RGBA_BLUE])  / 255.0F;
+					dst_pixel->alpha = (float)(src_pixel[FI_RGBA_ALPHA]) / 255.0F;
+
+					src_pixel += bytespp;
+					dst_pixel++;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const WORD *src_pixel = (WORD*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					const float dst_value = (float)src_pixel[x] / 65535.0F;
+					dst_pixel[x].red   = dst_value;
+					dst_pixel[x].green = dst_value;
+					dst_pixel[x].blue  = dst_value;
+					dst_pixel[x].alpha = 1.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x].red   = (float)(src_pixel[x].red)   / 65535.0F;
+					dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+					dst_pixel[x].blue  = (float)(src_pixel[x].blue)  / 65535.0F;
+					dst_pixel[x].alpha = 1.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x].red   = (float)(src_pixel[x].red)   / 65535.0F;
+					dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+					dst_pixel[x].blue  = (float)(src_pixel[x].blue)  / 65535.0F;
+					dst_pixel[x].alpha = (float)(src_pixel[x].alpha) / 65535.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_FLOAT:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const float *src_pixel = (float*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert by copying greyscale channel to each R, G, B channels
+					// assume float values are in [0..1]
+					const float value = CLAMP(src_pixel[x], 0.0F, 1.0F);
+					dst_pixel[x].red   = value;
+					dst_pixel[x].green = value;
+					dst_pixel[x].blue  = value;
+					dst_pixel[x].alpha = 1.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBF:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBF *src_pixel = (FIRGBF*)src_bits;
+				FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert pixels directly, while adding a "dummy" alpha of 1.0
+					dst_pixel[x].red   = CLAMP(src_pixel[x].red, 0.0F, 1.0F);
+					dst_pixel[x].green = CLAMP(src_pixel[x].green, 0.0F, 1.0F);
+					dst_pixel[x].blue  = CLAMP(src_pixel[x].blue, 0.0F, 1.0F);
+					dst_pixel[x].alpha = 1.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/ConversionRGBF.cpp b/files/Source/FreeImage/ConversionRGBF.cpp
new file mode 100644
index 0000000..230dd7b
--- /dev/null
+++ b/files/Source/FreeImage/ConversionRGBF.cpp
@@ -0,0 +1,243 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to RGBF
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBF(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// allow conversion from 24- and 32-bit
+			const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+			if((color_type != FIC_RGB) && (color_type != FIC_RGBALPHA)) {
+				src = FreeImage_ConvertTo24Bits(dib);
+				if(!src) return NULL;
+			} else {
+				src = dib;
+			}
+			break;
+		}
+		case FIT_UINT16:
+			// allow conversion from 16-bit
+			src = dib;
+			break;
+		case FIT_RGB16:
+			// allow conversion from 48-bit RGB
+			src = dib;
+			break;
+		case FIT_RGBA16:
+			// allow conversion from 64-bit RGBA (ignore the alpha channel)
+			src = dib;
+			break;
+		case FIT_FLOAT:
+			// allow conversion from 32-bit float
+			src = dib;
+			break;
+		case FIT_RGBAF:
+			// allow conversion from 128-bit RGBAF
+			src = dib;
+			break;
+		case FIT_RGBF:
+			// RGBF type : clone the src
+			return FreeImage_Clone(dib);
+			break;
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_RGBF, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to RGBF
+
+	const unsigned src_pitch = FreeImage_GetPitch(src);
+	const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE   *src_pixel = (BYTE*)src_bits;
+				FIRGBF *dst_pixel = (FIRGBF*)dst_bits;
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel->red   = (float)(src_pixel[FI_RGBA_RED])   / 255.0F;
+					dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F;
+					dst_pixel->blue  = (float)(src_pixel[FI_RGBA_BLUE])  / 255.0F;
+
+					src_pixel += bytespp;
+					dst_pixel ++;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_UINT16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const WORD *src_pixel = (WORD*)src_bits;
+				FIRGBF *dst_pixel = (FIRGBF*)dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					const float dst_value = (float)src_pixel[x] / 65535.0F;
+					dst_pixel[x].red   = dst_value;
+					dst_pixel[x].green = dst_value;
+					dst_pixel[x].blue  = dst_value;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGB16 *src_pixel = (FIRGB16*) src_bits;
+				FIRGBF  *dst_pixel = (FIRGBF*)  dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x].red   = (float)(src_pixel[x].red)   / 65535.0F;
+					dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+					dst_pixel[x].blue  = (float)(src_pixel[x].blue)  / 65535.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBA16 *src_pixel = (FIRGBA16*) src_bits;
+				FIRGBF  *dst_pixel = (FIRGBF*)  dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and scale to the range [0..1]
+					dst_pixel[x].red   = (float)(src_pixel[x].red)   / 65535.0F;
+					dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+					dst_pixel[x].blue  = (float)(src_pixel[x].blue)  / 65535.0F;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_FLOAT:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const float *src_pixel = (float*) src_bits;
+				FIRGBF  *dst_pixel = (FIRGBF*)  dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert by copying greyscale channel to each R, G, B channels
+					// assume float values are in [0..1]
+					const float value = CLAMP(src_pixel[x], 0.0F, 1.0F);
+					dst_pixel[x].red   = value;
+					dst_pixel[x].green = value;
+					dst_pixel[x].blue  = value;
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+
+		case FIT_RGBAF:
+		{
+			const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+			BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBAF *src_pixel = (FIRGBAF*) src_bits;
+				FIRGBF  *dst_pixel = (FIRGBF*)  dst_bits;
+
+				for(unsigned x = 0; x < width; x++) {
+					// convert and skip alpha channel
+					dst_pixel[x].red   = CLAMP(src_pixel[x].red, 0.0F, 1.0F);
+					dst_pixel[x].green = CLAMP(src_pixel[x].green, 0.0F, 1.0F);
+					dst_pixel[x].blue  = CLAMP(src_pixel[x].blue, 0.0F, 1.0F);
+				}
+				src_bits += src_pitch;
+				dst_bits += dst_pitch;
+			}
+		}
+		break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/ConversionType.cpp b/files/Source/FreeImage/ConversionType.cpp
new file mode 100644
index 0000000..a2ca90f
--- /dev/null
+++ b/files/Source/FreeImage/ConversionType.cpp
@@ -0,0 +1,699 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Tanner Helland (tannerhelland@users.sf.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+/** Convert a greyscale image of type Tsrc to type Tdst.
+	Conversion is done using standard C language casting convention.
+*/
+template<class Tdst, class Tsrc>
+class CONVERT_TYPE
+{
+public:
+	FIBITMAP* convert(FIBITMAP *src, FREE_IMAGE_TYPE dst_type);
+};
+
+template<class Tdst, class Tsrc> FIBITMAP* 
+CONVERT_TYPE<Tdst, Tsrc>::convert(FIBITMAP *src, FREE_IMAGE_TYPE dst_type) {
+
+	FIBITMAP *dst = NULL;
+
+	unsigned width	= FreeImage_GetWidth(src);
+	unsigned height = FreeImage_GetHeight(src);
+	unsigned bpp	= FreeImage_GetBPP(src);
+
+	// allocate dst image
+
+	dst = FreeImage_AllocateT(dst_type, width, height, bpp, 
+			FreeImage_GetRedMask(src), FreeImage_GetGreenMask(src), FreeImage_GetBlueMask(src));
+	if(!dst) return NULL;
+
+	// convert from src_type to dst_type
+	
+	for(unsigned y = 0; y < height; y++) {
+		const Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+		Tdst *dst_bits = reinterpret_cast<Tdst*>(FreeImage_GetScanLine(dst, y));
+
+		for(unsigned x = 0; x < width; x++) {
+			*dst_bits++ = static_cast<Tdst>(*src_bits++);
+		}
+	}
+
+	return dst;
+}
+
+
+/** Convert a greyscale image of type Tsrc to a 8-bit grayscale dib.
+	Conversion is done using either a linear scaling from [min, max] to [0, 255]
+	or a rounding from src_pixel to (BYTE) MIN(255, MAX(0, q)) where int q = int(src_pixel + 0.5); 
+*/
+template<class Tsrc>
+class CONVERT_TO_BYTE
+{
+public:
+	FIBITMAP* convert(FIBITMAP *src, BOOL scale_linear);
+};
+
+template<class Tsrc> FIBITMAP* 
+CONVERT_TO_BYTE<Tsrc>::convert(FIBITMAP *src, BOOL scale_linear) {
+	FIBITMAP *dst = NULL;
+	unsigned x, y;
+
+	unsigned width	= FreeImage_GetWidth(src);
+	unsigned height = FreeImage_GetHeight(src);
+
+	// allocate a 8-bit dib
+
+	dst = FreeImage_AllocateT(FIT_BITMAP, width, height, 8, 0, 0, 0);
+	if(!dst) return NULL;
+
+	// build a greyscale palette
+	RGBQUAD *pal = FreeImage_GetPalette(dst);
+	for(int i = 0; i < 256; i++) {
+		pal[i].rgbRed = (BYTE)i;
+		pal[i].rgbGreen = (BYTE)i;
+		pal[i].rgbBlue = (BYTE)i;
+	}
+
+	// convert the src image to dst
+	// (FIBITMAP are stored upside down)
+	if(scale_linear) {
+		Tsrc max, min;
+		double scale;
+
+		// find the min and max value of the image
+		Tsrc l_min, l_max;
+		min = 255, max = 0;
+		for(y = 0; y < height; y++) {
+			Tsrc *bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+			MAXMIN(bits, width, l_max, l_min);
+			if(l_max > max) max = l_max;
+			if(l_min < min) min = l_min;
+		}
+		if(max == min) {
+			max = 255; min = 0;
+		}
+
+		// compute the scaling factor
+		scale = 255 / (double)(max - min);
+
+		// scale to 8-bit
+		for(y = 0; y < height; y++) {
+			Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+			for(x = 0; x < width; x++) {
+				dst_bits[x] = (BYTE)( scale * (src_bits[x] - min) + 0.5);
+			}
+		}
+	} else {
+		for(y = 0; y < height; y++) {
+			Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+			BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+			for(x = 0; x < width; x++) {
+				// rounding
+				int q = int(src_bits[x] + 0.5);
+				dst_bits[x] = (BYTE) MIN(255, MAX(0, q));
+			}
+		}
+	}
+
+	return dst;
+}
+
+/** Convert a greyscale image of type Tsrc to a FICOMPLEX dib.
+*/
+template<class Tsrc>
+class CONVERT_TO_COMPLEX
+{
+public:
+	FIBITMAP* convert(FIBITMAP *src);
+};
+
+template<class Tsrc> FIBITMAP* 
+CONVERT_TO_COMPLEX<Tsrc>::convert(FIBITMAP *src) {
+	FIBITMAP *dst = NULL;
+
+	unsigned width	= FreeImage_GetWidth(src);
+	unsigned height = FreeImage_GetHeight(src);
+
+	// allocate dst image
+
+	dst = FreeImage_AllocateT(FIT_COMPLEX, width, height);
+	if(!dst) return NULL;
+
+	// convert from src_type to FIT_COMPLEX
+	
+	for(unsigned y = 0; y < height; y++) {
+		const Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+		FICOMPLEX *dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+
+		for(unsigned x = 0; x < width; x++) {
+			dst_bits[x].r = (double)src_bits[x];
+			dst_bits[x].i = 0;
+		}
+	}
+
+	return dst;
+}
+
+// ----------------------------------------------------------
+
+// Convert from type BYTE to type X
+CONVERT_TYPE<unsigned short, BYTE>	convertByteToUShort;
+CONVERT_TYPE<short, BYTE>			convertByteToShort;
+CONVERT_TYPE<DWORD, BYTE>			convertByteToULong;
+CONVERT_TYPE<LONG, BYTE>			convertByteToLong;
+CONVERT_TYPE<float, BYTE>			convertByteToFloat;
+CONVERT_TYPE<double, BYTE>			convertByteToDouble;
+
+// Convert from type X to type BYTE
+CONVERT_TO_BYTE<unsigned short>	convertUShortToByte;
+CONVERT_TO_BYTE<short>			convertShortToByte;
+CONVERT_TO_BYTE<DWORD>			convertULongToByte;
+CONVERT_TO_BYTE<LONG>			convertLongToByte;
+CONVERT_TO_BYTE<float>			convertFloatToByte;
+CONVERT_TO_BYTE<double>			convertDoubleToByte;
+
+// Convert from type X to type float
+CONVERT_TYPE<float, unsigned short>	convertUShortToFloat;
+CONVERT_TYPE<float, short>			convertShortToFloat;
+CONVERT_TYPE<float, DWORD>			convertULongToFloat;
+CONVERT_TYPE<float, LONG>			convertLongToFloat;
+
+// Convert from type X to type double
+CONVERT_TYPE<double, unsigned short>	convertUShortToDouble;
+CONVERT_TYPE<double, short>				convertShortToDouble;
+CONVERT_TYPE<double, DWORD>				convertULongToDouble;
+CONVERT_TYPE<double, LONG>				convertLongToDouble;
+CONVERT_TYPE<double, float>				convertFloatToDouble;
+
+// Convert from type X to type FICOMPLEX
+CONVERT_TO_COMPLEX<BYTE>			convertByteToComplex;
+CONVERT_TO_COMPLEX<unsigned short>	convertUShortToComplex;
+CONVERT_TO_COMPLEX<short>			convertShortToComplex;
+CONVERT_TO_COMPLEX<DWORD>			convertULongToComplex;
+CONVERT_TO_COMPLEX<LONG>			convertLongToComplex;
+CONVERT_TO_COMPLEX<float>			convertFloatToComplex;
+CONVERT_TO_COMPLEX<double>			convertDoubleToComplex;
+
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+//   smart convert X to standard FIBITMAP
+// ----------------------------------------------------------
+
+/** Convert image of any type to a standard 8-bit greyscale image.
+For standard images, a clone of the input image is returned.
+When the scale_linear parameter is TRUE, conversion is done by scaling linearly 
+each pixel to an integer value between [0..255]. When it is FALSE, conversion is done 
+by rounding each float pixel to an integer between [0..255]. 
+For complex images, the magnitude is extracted as a double image, then converted according to the scale parameter. 
+@param image Image to convert
+@param scale_linear Linear scaling / rounding switch
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear) {
+	FIBITMAP *dst = NULL;
+
+	if(!src) return NULL;
+
+	// convert from src_type to FIT_BITMAP
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+
+	switch(src_type) {
+		case FIT_BITMAP:	// standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+			dst = FreeImage_Clone(src);
+			break;
+		case FIT_UINT16:	// array of unsigned short: unsigned 16-bit
+			dst = convertUShortToByte.convert(src, scale_linear);
+			break;
+		case FIT_INT16:		// array of short: signed 16-bit
+			dst = convertShortToByte.convert(src, scale_linear);
+			break;
+		case FIT_UINT32:	// array of unsigned long: unsigned 32-bit
+			dst = convertULongToByte.convert(src, scale_linear);
+			break;
+		case FIT_INT32:		// array of long: signed 32-bit
+			dst = convertLongToByte.convert(src, scale_linear);
+			break;
+		case FIT_FLOAT:		// array of float: 32-bit
+			dst = convertFloatToByte.convert(src, scale_linear);
+			break;
+		case FIT_DOUBLE:	// array of double: 64-bit
+			dst = convertDoubleToByte.convert(src, scale_linear);
+			break;
+		case FIT_COMPLEX:	// array of FICOMPLEX: 2 x 64-bit
+			{
+				// Convert to type FIT_DOUBLE
+				FIBITMAP *dib_double = FreeImage_GetComplexChannel(src, FICC_MAG);
+				if(dib_double) {
+					// Convert to a standard bitmap (linear scaling)
+					dst = convertDoubleToByte.convert(dib_double, scale_linear);
+					// Free image of type FIT_DOUBLE
+					FreeImage_Unload(dib_double);
+				}
+			}
+			break;
+		case FIT_RGB16:		// 48-bit RGB image: 3 x 16-bit
+			break;
+		case FIT_RGBA16:	// 64-bit RGBA image: 4 x 16-bit
+			break;
+		case FIT_RGBF:		// 96-bit RGB float image: 3 x 32-bit IEEE floating point
+			break;
+		case FIT_RGBAF:		// 128-bit RGBA float image: 4 x 32-bit IEEE floating point
+			break;
+	}
+
+	if(NULL == dst) {
+		FreeImage_OutputMessageProc(FIF_UNKNOWN, "FREE_IMAGE_TYPE: Unable to convert from type %d to type %d.\n No such conversion exists.", src_type, FIT_BITMAP);
+	} else {
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+	}
+	
+	return dst;
+}
+
+
+
+// ----------------------------------------------------------
+//   smart convert X to Y
+// ----------------------------------------------------------
+
+FIBITMAP* DLL_CALLCONV
+FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear) {
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(src)) return NULL;
+
+	// convert from src_type to dst_type
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+
+	if(src_type == dst_type) {
+		return FreeImage_Clone(src);
+	}
+
+	const unsigned src_bpp = FreeImage_GetBPP(src);
+
+	switch(src_type) {
+		case FIT_BITMAP:
+			switch(dst_type) {
+				case FIT_UINT16:
+					dst = FreeImage_ConvertToUINT16(src);
+					break;
+				case FIT_INT16:
+					dst = (src_bpp == 8) ? convertByteToShort.convert(src, dst_type) : NULL;
+					break;
+				case FIT_UINT32:
+					dst = (src_bpp == 8) ? convertByteToULong.convert(src, dst_type) : NULL;
+					break;
+				case FIT_INT32:
+					dst = (src_bpp == 8) ? convertByteToLong.convert(src, dst_type) : NULL;
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					dst = (src_bpp == 8) ? convertByteToDouble.convert(src, dst_type) : NULL;
+					break;
+				case FIT_COMPLEX:
+					dst = (src_bpp == 8) ? convertByteToComplex.convert(src) : NULL;
+					break;
+				case FIT_RGB16:
+					dst = FreeImage_ConvertToRGB16(src);
+					break;
+				case FIT_RGBA16:
+					dst = FreeImage_ConvertToRGBA16(src);
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_UINT16:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					dst = convertUShortToDouble.convert(src, dst_type);
+					break;
+				case FIT_COMPLEX:
+					dst = convertUShortToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					dst = FreeImage_ConvertToRGB16(src);
+					break;
+				case FIT_RGBA16:
+					dst = FreeImage_ConvertToRGBA16(src);
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_INT16:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = convertShortToFloat.convert(src, dst_type);
+					break;
+				case FIT_DOUBLE:
+					dst = convertShortToDouble.convert(src, dst_type);
+					break;
+				case FIT_COMPLEX:
+					dst = convertShortToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					break;
+				case FIT_RGBAF:
+					break;
+			}
+			break;
+		case FIT_UINT32:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = convertULongToFloat.convert(src, dst_type);
+					break;
+				case FIT_DOUBLE:
+					dst = convertULongToDouble.convert(src, dst_type);
+					break;
+				case FIT_COMPLEX:
+					dst = convertULongToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					break;
+				case FIT_RGBAF:
+					break;
+			}
+			break;
+		case FIT_INT32:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_FLOAT:
+					dst = convertLongToFloat.convert(src, dst_type);
+					break;
+				case FIT_DOUBLE:
+					dst = convertLongToDouble.convert(src, dst_type);
+					break;
+				case FIT_COMPLEX:
+					dst = convertLongToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					break;
+				case FIT_RGBAF:
+					break;
+			}
+			break;
+		case FIT_FLOAT:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_DOUBLE:
+					dst = convertFloatToDouble.convert(src, dst_type);
+					break;
+				case FIT_COMPLEX:
+					dst = convertFloatToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_DOUBLE:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertToStandardType(src, scale_linear);
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					break;
+				case FIT_COMPLEX:
+					dst = convertDoubleToComplex.convert(src);
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					break;
+				case FIT_RGBAF:
+					break;
+			}
+			break;
+		case FIT_COMPLEX:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					break;
+				case FIT_DOUBLE:
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					break;
+				case FIT_RGBAF:
+					break;
+			}
+			break;
+		case FIT_RGB16:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertTo24Bits(src);
+					break;
+				case FIT_UINT16:
+					dst = FreeImage_ConvertToUINT16(src);
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					break;
+				case FIT_COMPLEX:
+					break;
+				case FIT_RGBA16:
+					dst = FreeImage_ConvertToRGBA16(src);
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_RGBA16:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					dst = FreeImage_ConvertTo32Bits(src);
+					break;
+				case FIT_UINT16:
+					dst = FreeImage_ConvertToUINT16(src);
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					break;
+				case FIT_COMPLEX:
+					break;
+				case FIT_RGB16:
+					dst = FreeImage_ConvertToRGB16(src);
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_RGBF:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					break;
+				case FIT_COMPLEX:
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBAF:
+					dst = FreeImage_ConvertToRGBAF(src);
+					break;
+			}
+			break;
+		case FIT_RGBAF:
+			switch(dst_type) {
+				case FIT_BITMAP:
+					break;
+				case FIT_UINT16:
+					break;
+				case FIT_INT16:
+					break;
+				case FIT_UINT32:
+					break;
+				case FIT_INT32:
+					break;
+				case FIT_FLOAT:
+					dst = FreeImage_ConvertToFloat(src);
+					break;
+				case FIT_DOUBLE:
+					break;
+				case FIT_COMPLEX:
+					break;
+				case FIT_RGB16:
+					break;
+				case FIT_RGBA16:
+					break;
+				case FIT_RGBF:
+					dst = FreeImage_ConvertToRGBF(src);
+					break;
+			}
+			break;
+	}
+
+	if(NULL == dst) {
+		FreeImage_OutputMessageProc(FIF_UNKNOWN, "FREE_IMAGE_TYPE: Unable to convert from type %d to type %d.\n No such conversion exists.", src_type, dst_type);
+	} else {
+		// copy metadata from src to dst
+		FreeImage_CloneMetadata(dst, src);
+	}
+
+	return dst;
+}
diff --git a/files/Source/FreeImage/ConversionUINT16.cpp b/files/Source/FreeImage/ConversionUINT16.cpp
new file mode 100644
index 0000000..e6492aa
--- /dev/null
+++ b/files/Source/FreeImage/ConversionUINT16.cpp
@@ -0,0 +1,134 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   smart convert X to UINT16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToUINT16(FIBITMAP *dib) {
+	FIBITMAP *src = NULL;
+	FIBITMAP *dst = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+	// check for allowed conversions 
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			// convert to greyscale if needed
+			if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) {
+				src = dib;
+			} else {
+				src = FreeImage_ConvertToGreyscale(dib);
+				if(!src) return NULL;
+			}
+			break;
+		}
+		case FIT_UINT16:
+			// UINT16 type : clone the src
+			return FreeImage_Clone(dib);
+			break;
+		case FIT_RGB16:
+			// allow conversion from 48-bit RGB
+			src = dib;
+			break;
+		case FIT_RGBA16:
+			// allow conversion from 64-bit RGBA (ignore the alpha channel)
+			src = dib;
+			break;
+		default:
+			return NULL;
+	}
+
+	// allocate dst image
+
+	const unsigned width = FreeImage_GetWidth(src);
+	const unsigned height = FreeImage_GetHeight(src);
+
+	dst = FreeImage_AllocateT(FIT_UINT16, width, height);
+	if(!dst) {
+		if(src != dib) {
+			FreeImage_Unload(src);
+		}
+		return NULL;
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(dst, src);
+
+	// convert from src type to UINT16
+
+	switch(src_type) {
+		case FIT_BITMAP:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					dst_bits[x] = src_bits[x] << 8;
+				}
+			}
+		}
+		break;
+
+		case FIT_RGB16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert to grey
+					dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue);
+				}
+			}
+		}
+		break;
+
+		case FIT_RGBA16:
+		{
+			for(unsigned y = 0; y < height; y++) {
+				const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y);
+				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+				for(unsigned x = 0; x < width; x++) {
+					// convert to grey
+					dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue);
+				}
+			}
+		}
+		break;
+
+		default:
+			break;
+	}
+
+	if(src != dib) {
+		FreeImage_Unload(src);
+	}
+
+	return dst;
+}
+
diff --git a/files/Source/FreeImage/FreeImage.cpp b/files/Source/FreeImage/FreeImage.cpp
new file mode 100644
index 0000000..2de6077
--- /dev/null
+++ b/files/Source/FreeImage/FreeImage.cpp
@@ -0,0 +1,226 @@
+// ==========================================================
+// FreeImage implementation
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+//----------------------------------------------------------------------
+
+static const char *s_copyright = "This program uses FreeImage, a free, open source image library supporting all common bitmap formats. See http://freeimage.sourceforge.net for details";
+
+//----------------------------------------------------------------------
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+#ifndef FREEIMAGE_LIB
+
+BOOL APIENTRY
+DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
+	switch (ul_reason_for_call) {
+		case DLL_PROCESS_ATTACH :
+			FreeImage_Initialise(FALSE);
+			break;
+
+		case DLL_PROCESS_DETACH :
+			FreeImage_DeInitialise();
+			break;
+
+		case DLL_THREAD_ATTACH :
+		case DLL_THREAD_DETACH :
+			break;
+    }
+
+    return TRUE;
+}
+
+#endif // FREEIMAGE_LIB
+
+#else // !_WIN32 
+#ifndef FREEIMAGE_LIB
+
+void FreeImage_SO_Initialise() __attribute__((constructor));
+void FreeImage_SO_DeInitialise() __attribute__((destructor));
+
+void FreeImage_SO_Initialise() {
+  FreeImage_Initialise(FALSE);
+}
+
+void FreeImage_SO_DeInitialise() {
+  FreeImage_DeInitialise();
+}
+#endif // FREEIMAGE_LIB
+
+#endif // _WIN32
+
+//----------------------------------------------------------------------
+
+const char * DLL_CALLCONV
+FreeImage_GetVersion() {
+	static char s_version[16];
+	sprintf(s_version, "%d.%d.%d", FREEIMAGE_MAJOR_VERSION, FREEIMAGE_MINOR_VERSION, FREEIMAGE_RELEASE_SERIAL);
+	return s_version;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetCopyrightMessage() {
+	return s_copyright;
+}
+
+//----------------------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_IsLittleEndian() {
+	union {
+		DWORD i;
+		BYTE c[4];
+	} u;
+	u.i = 1;
+	return (u.c[0] != 0);
+}
+
+//----------------------------------------------------------------------
+
+static FreeImage_OutputMessageFunction freeimage_outputmessage_proc = NULL;
+static FreeImage_OutputMessageFunctionStdCall freeimage_outputmessagestdcall_proc = NULL; 
+
+void DLL_CALLCONV
+FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf) {
+	freeimage_outputmessage_proc = omf;
+}
+
+void DLL_CALLCONV
+FreeImage_SetOutputMessageStdCall(FreeImage_OutputMessageFunctionStdCall omf) {
+	freeimage_outputmessagestdcall_proc = omf;
+}
+
+void DLL_CALLCONV
+FreeImage_OutputMessageProc(int fif, const char *fmt, ...) {
+	const int MSG_SIZE = 512; // 512 bytes should be more than enough for a short message
+
+	if ((fmt != NULL) && ((freeimage_outputmessage_proc != NULL) || (freeimage_outputmessagestdcall_proc != NULL))) {
+		char message[MSG_SIZE];
+		memset(message, 0, MSG_SIZE);
+
+		// initialize the optional parameter list
+
+		va_list arg;
+		va_start(arg, fmt);
+
+		// check the length of the format string
+
+		int str_length = (int)( (strlen(fmt) > MSG_SIZE) ? MSG_SIZE : strlen(fmt) );
+
+		// parse the format string and put the result in 'message'
+
+		for (int i = 0, j = 0; i < str_length; ++i) {
+			if (fmt[i] == '%') {
+				if (i + 1 < str_length) {
+					switch(tolower(fmt[i + 1])) {
+						case '%' :
+							message[j++] = '%';
+							break;
+
+						case 'o' : // octal numbers
+						{
+							char tmp[16];
+
+							_itoa(va_arg(arg, int), tmp, 8);
+
+							strcat(message, tmp);
+
+							j += (int)strlen(tmp);
+
+							++i;
+
+							break;
+						}
+
+						case 'i' : // decimal numbers
+						case 'd' :
+						{
+							char tmp[16];
+
+							_itoa(va_arg(arg, int), tmp, 10);
+
+							strcat(message, tmp);
+
+							j += (int)strlen(tmp);
+
+							++i;
+
+							break;
+						}
+
+						case 'x' : // hexadecimal numbers
+						{
+							char tmp[16];
+
+							_itoa(va_arg(arg, int), tmp, 16);
+
+							strcat(message, tmp);
+
+							j += (int)strlen(tmp);
+
+							++i;
+
+							break;
+						}
+
+						case 's' : // strings
+						{
+							char *tmp = va_arg(arg, char*);
+
+							strcat(message, tmp);
+
+							j += (int)strlen(tmp);
+
+							++i;
+
+							break;
+						}
+					};
+				} else {
+					message[j++] = fmt[i];
+				}
+			} else {
+				message[j++] = fmt[i];
+			};
+		}
+
+		// deinitialize the optional parameter list
+
+		va_end(arg);
+
+		// output the message to the user program
+
+		if (freeimage_outputmessage_proc != NULL)
+			freeimage_outputmessage_proc((FREE_IMAGE_FORMAT)fif, message);
+
+		if (freeimage_outputmessagestdcall_proc != NULL)
+			freeimage_outputmessagestdcall_proc((FREE_IMAGE_FORMAT)fif, message); 
+	}
+}
diff --git a/files/Source/FreeImage/FreeImageC.c b/files/Source/FreeImage/FreeImageC.c
new file mode 100644
index 0000000..9e81259
--- /dev/null
+++ b/files/Source/FreeImage/FreeImageC.c
@@ -0,0 +1,22 @@
+// ==========================================================
+// Use from c compiler test file
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
diff --git a/files/Source/FreeImage/FreeImageIO.cpp b/files/Source/FreeImage/FreeImageIO.cpp
new file mode 100644
index 0000000..83394f0
--- /dev/null
+++ b/files/Source/FreeImage/FreeImageIO.cpp
@@ -0,0 +1,175 @@
+// ==========================================================
+// Input/Output functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
+// =====================================================================
+// File IO functions
+// =====================================================================
+
+unsigned DLL_CALLCONV 
+_ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+	return (unsigned)fread(buffer, size, count, (FILE *)handle);
+}
+
+unsigned DLL_CALLCONV 
+_WriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+	return (unsigned)fwrite(buffer, size, count, (FILE *)handle);
+}
+
+int DLL_CALLCONV
+_SeekProc(fi_handle handle, long offset, int origin) {
+	return fseek((FILE *)handle, offset, origin);
+}
+
+long DLL_CALLCONV
+_TellProc(fi_handle handle) {
+	return ftell((FILE *)handle);
+}
+
+// ----------------------------------------------------------
+
+void
+SetDefaultIO(FreeImageIO *io) {
+	io->read_proc  = _ReadProc;
+	io->seek_proc  = _SeekProc;
+	io->tell_proc  = _TellProc;
+	io->write_proc = _WriteProc;
+}
+
+// =====================================================================
+// Memory IO functions
+// =====================================================================
+
+unsigned DLL_CALLCONV 
+_MemoryReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+	unsigned x;
+
+	FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+	for(x = 0; x < count; x++) {
+		long remaining_bytes = mem_header->file_length - mem_header->current_position;
+		//if there isn't size bytes left to read, set pos to eof and return a short count
+		if( remaining_bytes < (long)size ) {
+			if(remaining_bytes > 0) {
+				memcpy( buffer, (char *)mem_header->data + mem_header->current_position, remaining_bytes );
+			}
+			mem_header->current_position = mem_header->file_length;
+			break;
+		}
+		//copy size bytes count times
+		memcpy( buffer, (char *)mem_header->data + mem_header->current_position, size );
+		mem_header->current_position += size;
+		buffer = (char *)buffer + size;
+	}
+	return x;
+}
+
+unsigned DLL_CALLCONV 
+_MemoryWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+	void *newdata;
+	long newdatalen;
+
+	FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+	//double the data block size if we need to
+	while( (mem_header->current_position + (long)(size * count)) >= mem_header->data_length ) {
+		//if we are at or above 1G, we cant double without going negative
+		if( mem_header->data_length & 0x40000000 ) {
+			//max 2G
+			if( mem_header->data_length == 0x7FFFFFFF ) {
+				return 0;
+			}
+			newdatalen = 0x7FFFFFFF;
+		} else if( mem_header->data_length == 0 ) {
+			//default to 4K if nothing yet
+			newdatalen = 4096;
+		} else {
+			//double size
+			newdatalen = mem_header->data_length << 1;
+		}
+		newdata = realloc( mem_header->data, newdatalen );
+		if( !newdata ) {
+			return 0;
+		}
+		mem_header->data = newdata;
+		mem_header->data_length = newdatalen;
+	}
+	memcpy( (char *)mem_header->data + mem_header->current_position, buffer, size * count );
+	mem_header->current_position += size * count;
+	if( mem_header->current_position > mem_header->file_length ) {
+		mem_header->file_length = mem_header->current_position;
+	}
+	return count;
+}
+
+int DLL_CALLCONV 
+_MemorySeekProc(fi_handle handle, long offset, int origin) {
+	FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+	// you can use _MemorySeekProc to reposition the pointer anywhere in a file
+	// the pointer can also be positioned beyond the end of the file
+
+	switch(origin) { //0 to filelen-1 are 'inside' the file
+		default:
+		case SEEK_SET: //can fseek() to 0-7FFFFFFF always
+			if( offset >= 0 ) {
+				mem_header->current_position = offset;
+				return 0;
+			}
+			break;
+
+		case SEEK_CUR:
+			if( mem_header->current_position + offset >= 0 ) {
+				mem_header->current_position += offset;
+				return 0;
+			}
+			break;
+
+		case SEEK_END:
+			if( mem_header->file_length + offset >= 0 ) {
+				mem_header->current_position = mem_header->file_length + offset;
+				return 0;
+			}
+			break;
+	}
+
+	return -1;
+}
+
+long DLL_CALLCONV 
+_MemoryTellProc(fi_handle handle) {
+	FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+	return mem_header->current_position;
+}
+
+// ----------------------------------------------------------
+
+void
+SetMemoryIO(FreeImageIO *io) {
+	io->read_proc  = _MemoryReadProc;
+	io->seek_proc  = _MemorySeekProc;
+	io->tell_proc  = _MemoryTellProc;
+	io->write_proc = _MemoryWriteProc;
+}
diff --git a/files/Source/FreeImage/GetType.cpp b/files/Source/FreeImage/GetType.cpp
new file mode 100644
index 0000000..9ffbec7
--- /dev/null
+++ b/files/Source/FreeImage/GetType.cpp
@@ -0,0 +1,94 @@
+// ==========================================================
+// GetType
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif 
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+#include "../DeprecationManager/DeprecationMgr.h"
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileTypeFromHandle(FreeImageIO *io, fi_handle handle, int size) {
+	if (handle != NULL) {
+		int fif_count = FreeImage_GetFIFCount();
+
+		for (int i = 0; i < fif_count; ++i) {
+			FREE_IMAGE_FORMAT fif = (FREE_IMAGE_FORMAT)i;
+			if (FreeImage_Validate(fif, io, handle)) {
+#ifndef IG_TARGET_EMBEDDED
+				if(fif == FIF_TIFF) {
+					// many camera raw files use a TIFF signature ...
+					// ... try to revalidate against FIF_RAW (even if it breaks the code genericity)
+					if (FreeImage_Validate(FIF_RAW, io, handle)) {
+						return FIF_RAW;
+					}
+				}
+#endif // IG_TARGET_EMBEDDED
+				return fif;
+			}
+		}
+	}
+
+	return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileType(const char *filename, int size) {
+	FreeImageIO io;
+	SetDefaultIO(&io);
+	
+	FILE *handle = fopen(filename, "rb");
+
+	if (handle != NULL) {
+		FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)handle, size);
+
+		fclose(handle);
+
+		return format;
+	}
+
+	return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV 
+FreeImage_GetFileTypeU(const wchar_t *filename, int size) {
+#ifdef _WIN32	
+	FreeImageIO io;
+	SetDefaultIO(&io);
+	FILE *handle = _wfopen(filename, L"rb");
+
+	if (handle != NULL) {
+		FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)handle, size);
+
+		fclose(handle);
+
+		return format;
+	}
+#endif
+	return FIF_UNKNOWN;
+}
+
diff --git a/files/Source/FreeImage/Halftoning.cpp b/files/Source/FreeImage/Halftoning.cpp
new file mode 100644
index 0000000..313cc26
--- /dev/null
+++ b/files/Source/FreeImage/Halftoning.cpp
@@ -0,0 +1,474 @@
+// ==========================================================
+// Bitmap conversion routines
+// Thresholding and halftoning functions
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Dennis Lim (dlkj@users.sourceforge.net)
+// - Thomas Chmielewski (Chmielewski.Thomas@oce.de)
+//
+// Main reference : Ulichney, R., Digital Halftoning, The MIT Press, Cambridge, MA, 1987
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+static const int WHITE = 255;
+static const int BLACK = 0;
+
+// Floyd & Steinberg error diffusion dithering
+// This algorithm use the following filter
+//          *   7
+//      3   5   1     (1/16)
+static FIBITMAP* FloydSteinberg(FIBITMAP *dib) {
+
+#define RAND(RN) (((seed = 1103515245 * seed + 12345) >> 12) % (RN))
+#define INITERR(X, Y) (((int) X) - (((int) Y) ? WHITE : BLACK) + ((WHITE/2)-((int)X)) / 2)
+
+	int seed = 0;
+	int x, y, p, pixel, threshold, error;
+	int width, height, pitch;
+	BYTE *bits, *new_bits;
+	FIBITMAP *new_dib = NULL;
+
+	// allocate a 8-bit DIB
+	width = FreeImage_GetWidth(dib);
+	height = FreeImage_GetHeight(dib);
+	pitch = FreeImage_GetPitch(dib);
+	new_dib = FreeImage_Allocate(width, height, 8);
+	if(NULL == new_dib) return NULL;
+
+	// allocate space for error arrays
+	int *lerr = (int*)malloc (width * sizeof(int));
+	int *cerr = (int*)malloc (width * sizeof(int));
+	memset(lerr, 0, width * sizeof(int));
+	memset(cerr, 0, width * sizeof(int));
+
+	// left border
+	error = 0;
+	for(y = 0; y < height; y++) {
+		bits = FreeImage_GetScanLine(dib, y);
+		new_bits = FreeImage_GetScanLine(new_dib, y);
+
+		threshold = (WHITE / 2 + RAND(129) - 64);
+		pixel = bits[0] + error;
+		p = (pixel > threshold) ? WHITE : BLACK;
+		error = pixel - p;
+		new_bits[0] = (BYTE)p;
+	}
+	// right border
+	error = 0;
+	for(y = 0; y < height; y++) {
+		bits = FreeImage_GetScanLine(dib, y);
+		new_bits = FreeImage_GetScanLine(new_dib, y);
+
+		threshold = (WHITE / 2 + RAND(129) - 64);
+		pixel = bits[width-1] + error;
+		p = (pixel > threshold) ? WHITE : BLACK;
+		error = pixel - p;
+		new_bits[width-1] = (BYTE)p;
+	}
+	// top border
+	bits = FreeImage_GetBits(dib);
+	new_bits = FreeImage_GetBits(new_dib);
+	error = 0;
+	for(x = 0; x < width; x++) {
+		threshold = (WHITE / 2 + RAND(129) - 64);
+		pixel = bits[x] + error;
+		p = (pixel > threshold) ? WHITE : BLACK;
+		error = pixel - p;
+		new_bits[x] = (BYTE)p;
+		lerr[x] = INITERR(bits[x], p);
+	}
+
+	// interior bits
+	for(y = 1; y < height; y++) {
+		// scan left to right
+		bits = FreeImage_GetScanLine(dib, y);
+		new_bits = FreeImage_GetScanLine(new_dib, y);
+
+	    cerr[0] = INITERR(bits[0], new_bits[0]);
+		for(x = 1; x < width - 1; x++) {
+			error = (lerr[x-1] + 5 * lerr[x] + 3 * lerr[x+1] + 7 * cerr[x-1]) / 16;
+			pixel = bits[x] + error;
+			if(pixel > (WHITE / 2)) {		
+				new_bits[x] = WHITE;
+				cerr[x] = pixel - WHITE; 
+			} else {
+				new_bits[x] = BLACK;
+				cerr[x] = pixel - BLACK; 
+			}
+		}
+		// set errors for ends of the row
+		cerr[0] = INITERR (bits[0], new_bits[0]);
+		cerr[width - 1] = INITERR (bits[width - 1], new_bits[width - 1]);
+
+		// swap error buffers
+		int *terr = lerr; lerr = cerr; cerr = terr;
+	}
+
+	free(lerr);
+	free(cerr);
+
+	return new_dib;
+}
+
+// ==========================================================
+// Bayer ordered dispersed dot dithering
+//
+
+// Function taken from "Ordered Dithering, Stephen Hawley, Graphics Gems, Academic Press, 1990"
+// This function is used to generate a Bayer dithering matrice whose dimension are 2^size by 2^size
+//
+static int dithervalue(int x, int y, int size) {
+	int d = 0;
+	/*
+	 * calculate the dither value at a particular
+	 * (x, y) over the size of the matrix.
+	 */
+	while (size-->0)	{
+		/* Think of d as the density. At every iteration,
+		 * d is shifted left one and a new bit is put in the
+		 * low bit based on x and y. If x is odd and y is even,
+		 * or x is even and y is odd, a bit is put in. This
+		 * generates the checkerboard seen in dithering.
+		 * This quantity is shifted left again and the low bit of
+		 * y is added in.
+		 * This whole thing interleaves a checkerboard bit pattern
+		 * and y's bits, which is the value you want.
+		 */
+		d = (d <<1 | (x&1 ^ y&1))<<1 | y&1;
+		x >>= 1;
+		y >>= 1;
+	}
+	return d;
+}
+
+// Ordered dithering with a Bayer matrix of size 2^order by 2^order
+//
+static FIBITMAP* OrderedDispersedDot(FIBITMAP *dib, int order) {
+	int x, y;
+	int width, height;
+	BYTE *bits, *new_bits;
+	FIBITMAP *new_dib = NULL;
+
+	// allocate a 8-bit DIB
+	width = FreeImage_GetWidth(dib);
+	height = FreeImage_GetHeight(dib);
+	new_dib = FreeImage_Allocate(width, height, 8);
+	if(NULL == new_dib) return NULL;
+
+	// build the dithering matrix
+	int l = (1 << order);	// square of dither matrix order; the dimensions of the matrix
+	BYTE *matrix = (BYTE*)malloc(l*l * sizeof(BYTE));
+	for(int i = 0; i < l*l; i++) {
+		// according to "Purdue University: Digital Image Processing Laboratory: Image Halftoning, April 30th, 2006
+		matrix[i] = (BYTE)( 255 * (((double)dithervalue(i / l, i % l, order) + 0.5) / (l*l)) );
+	}
+
+	// perform the dithering
+	for(y = 0; y < height; y++) {
+		// scan left to right
+		bits = FreeImage_GetScanLine(dib, y);
+		new_bits = FreeImage_GetScanLine(new_dib, y);
+		for(x = 0; x < width; x++) {
+			if(bits[x] > matrix[(x % l) + l * (y % l)]) {
+				new_bits[x] = WHITE;
+			} else {
+				new_bits[x] = BLACK;
+			}
+		}
+	}
+
+	free(matrix);
+
+	return new_dib;
+}
+
+// ==========================================================
+// Ordered clustered dot dithering
+//
+
+// NB : The predefined dither matrices are the same as matrices used in 
+// the Netpbm package (http://netpbm.sourceforge.net) and are defined in Ulichney's book.
+// See also : The newsprint web site at http://www.cl.cam.ac.uk/~and1000/newsprint/
+// for more technical info on this dithering technique
+//
+static FIBITMAP* OrderedClusteredDot(FIBITMAP *dib, int order) {
+	// Order-3 clustered dithering matrix.
+	int cluster3[] = {
+	  9,11,10, 8, 6, 7,
+	  12,17,16, 5, 0, 1,
+	  13,14,15, 4, 3, 2,
+	  8, 6, 7, 9,11,10,
+	  5, 0, 1,12,17,16,
+	  4, 3, 2,13,14,15
+	};
+
+	// Order-4 clustered dithering matrix. 
+	int cluster4[] = {
+	  18,20,19,16,13,11,12,15,
+	  27,28,29,22, 4, 3, 2, 9,
+	  26,31,30,21, 5, 0, 1,10,
+	  23,25,24,17, 8, 6, 7,14,
+	  13,11,12,15,18,20,19,16,
+	  4, 3, 2, 9,27,28,29,22,
+	  5, 0, 1,10,26,31,30,21,
+	  8, 6, 7,14,23,25,24,17
+	};
+
+	// Order-8 clustered dithering matrix. 
+	int cluster8[] = {
+	   64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60,
+	   70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52,
+	   78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44,
+	   88,110,123,124,125,118,107, 85, 39, 17,  4,  3,  2,  9, 20, 42,
+	   89,111,122,127,126,117,106, 84, 38, 16,  5,  0,  1, 10, 21, 43,
+	   79,102,119,121,120,113, 97, 82, 48, 25,  8,  6,  7, 14, 30, 45,
+	   71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53,
+	   65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61,
+	   63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67,
+	   57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75,
+	   49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83,
+	   39, 17,  4,  3,  2,  9, 20, 42, 88,110,123,124,125,118,107, 85,
+	   38, 16,  5,  0,  1, 10, 21, 43, 89,111,122,127,126,117,106, 84,
+	   48, 25,  8,  6,  7, 14, 30, 45, 79,102,119,121,120,113, 97, 82,
+	   56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74,
+	   62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66
+	};
+
+	int x, y, pixel;
+	int width, height;
+	BYTE *bits, *new_bits;
+	FIBITMAP *new_dib = NULL;
+
+	// allocate a 8-bit DIB
+	width = FreeImage_GetWidth(dib);
+	height = FreeImage_GetHeight(dib);
+	new_dib = FreeImage_Allocate(width, height, 8);
+	if(NULL == new_dib) return NULL;
+
+	// select the dithering matrix
+	int *matrix = NULL;
+	switch(order) {
+		case 3:
+			matrix = &cluster3[0];
+			break;
+		case 4:
+			matrix = &cluster4[0];
+			break;
+		case 8:
+			matrix = &cluster8[0];
+			break;
+		default:
+			return NULL;
+	}
+
+	// scale the dithering matrix
+	int l = 2 * order;
+	int scale = 256 / (l * order);
+	for(y = 0; y < l; y++) {
+		for(x = 0; x < l; x++) {
+			matrix[y*l + x] *= scale;
+		}
+	}
+
+	// perform the dithering
+	for(y = 0; y < height; y++) {
+		// scan left to right
+		bits = FreeImage_GetScanLine(dib, y);
+		new_bits = FreeImage_GetScanLine(new_dib, y);
+		for(x = 0; x < width; x++) {
+			pixel = bits[x];
+			if(pixel >= matrix[(y % l) + l * (x % l)]) {
+				new_bits[x] = WHITE;
+			} else {
+				new_bits[x] = BLACK;
+			}
+		}
+	}
+
+	return new_dib;
+}
+
+
+// ==========================================================
+// Halftoning function
+//
+FIBITMAP * DLL_CALLCONV
+FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm) {
+	FIBITMAP *input = NULL, *dib8 = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const unsigned bpp = FreeImage_GetBPP(dib);
+
+	if(bpp == 1) {
+		// Just clone the dib and adjust the palette if needed
+		FIBITMAP *new_dib = FreeImage_Clone(dib);
+		if(NULL == new_dib) return NULL;
+		if(FreeImage_GetColorType(new_dib) == FIC_PALETTE) {
+			// Build a monochrome palette
+			RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+		}
+		return new_dib;
+	}
+
+	// Convert the input dib to a 8-bit greyscale dib
+	//
+	switch(bpp) {
+		case 8:
+			if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+				input = dib;
+			} else {
+				input = FreeImage_ConvertToGreyscale(dib);
+			} 
+			break;
+		case 4:
+		case 16:
+		case 24:
+		case 32:
+			input = FreeImage_ConvertToGreyscale(dib);
+			break;			
+	}
+	if(NULL == input) return NULL;
+
+	// Apply the dithering algorithm
+	switch(algorithm) {
+		case FID_FS:
+			dib8 = FloydSteinberg(input);
+			break;
+		case FID_BAYER4x4:
+			dib8 = OrderedDispersedDot(input, 2);
+			break;
+		case FID_BAYER8x8:
+			dib8 = OrderedDispersedDot(input, 3);
+			break;
+		case FID_BAYER16x16:
+			dib8 = OrderedDispersedDot(input, 4);
+			break;
+		case FID_CLUSTER6x6:
+			dib8 = OrderedClusteredDot(input, 3);
+			break;
+		case FID_CLUSTER8x8:
+			dib8 = OrderedClusteredDot(input, 4);
+			break;
+		case FID_CLUSTER16x16:
+			dib8 = OrderedClusteredDot(input, 8);
+			break;
+	}
+	if(input != dib) {
+		FreeImage_Unload(input);
+	}
+
+	// Build a greyscale palette (needed by threshold)
+	RGBQUAD *grey_pal = FreeImage_GetPalette(dib8);
+	for(int i = 0; i < 256; i++) {
+		grey_pal[i].rgbRed	= (BYTE)i;
+		grey_pal[i].rgbGreen = (BYTE)i;
+		grey_pal[i].rgbBlue	= (BYTE)i;
+	}
+
+	// Convert to 1-bit
+	FIBITMAP *new_dib = FreeImage_Threshold(dib8, 128);
+	FreeImage_Unload(dib8);
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(new_dib, dib);
+
+	return new_dib;
+}
+
+// ==========================================================
+// Thresholding function
+//
+FIBITMAP * DLL_CALLCONV
+FreeImage_Threshold(FIBITMAP *dib, BYTE T) {
+	FIBITMAP *dib8 = NULL;
+
+	if(!FreeImage_HasPixels(dib)) return NULL;
+
+	const unsigned bpp = FreeImage_GetBPP(dib);
+
+	if(bpp == 1) {
+		// Just clone the dib and adjust the palette if needed
+		FIBITMAP *new_dib = FreeImage_Clone(dib);
+		if(NULL == new_dib) return NULL;
+		if(FreeImage_GetColorType(new_dib) == FIC_PALETTE) {
+			// Build a monochrome palette
+			RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+			pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+			pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+		}
+		return new_dib;
+	}
+
+	// Convert the input dib to a 8-bit greyscale dib
+	//
+	switch(bpp) {
+		case 8:
+			if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+				dib8 = dib;
+			} else {
+				dib8 = FreeImage_ConvertToGreyscale(dib);
+			} 
+			break;
+		case 4:
+		case 16:
+		case 24:
+		case 32:
+			dib8 = FreeImage_ConvertToGreyscale(dib);
+			break;			
+	}
+	if(NULL == dib8) return NULL;
+
+	// Allocate a new 1-bit DIB
+	int width = FreeImage_GetWidth(dib);
+	int height = FreeImage_GetHeight(dib);
+	FIBITMAP *new_dib = FreeImage_Allocate(width, height, 1);
+	if(NULL == new_dib) return NULL;
+	// Build a monochrome palette
+	RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+	pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+	pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+	// Perform the thresholding
+	//
+	for(int y = 0; y < height; y++) {
+		BYTE *bits8 = FreeImage_GetScanLine(dib8, y);
+		BYTE *bits1 = FreeImage_GetScanLine(new_dib, y);
+		for(int x = 0; x < width; x++) {
+			if(bits8[x] < T) {
+				// Set bit(x, y) to 0
+				bits1[x >> 3] &= (0xFF7F >> (x & 0x7));
+			} else {
+				// Set bit(x, y) to 1
+				bits1[x >> 3] |= (0x80 >> (x & 0x7));
+			}
+		}
+	}
+	if(dib8 != dib) {
+		FreeImage_Unload(dib8);
+	}
+
+	// copy metadata from src to dst
+	FreeImage_CloneMetadata(new_dib, dib);
+
+	return new_dib;
+}
+
diff --git a/files/Source/FreeImage/J2KHelper.cpp b/files/Source/FreeImage/J2KHelper.cpp
new file mode 100644
index 0000000..e20979a
--- /dev/null
+++ b/files/Source/FreeImage/J2KHelper.cpp
@@ -0,0 +1,591 @@
+// ==========================================================
+// JPEG2000 helpers
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "third_party/openjpeg2/src/lib/openjp2/openjpeg.h"
+#include "J2KHelper.h"
+
+// --------------------------------------------------------------------------
+
+static OPJ_UINT64 
+_LengthProc(J2KFIO_t *fio) {
+	long start_pos = fio->io->tell_proc(fio->handle);
+	fio->io->seek_proc(fio->handle, 0, SEEK_END);
+	unsigned file_length = fio->io->tell_proc(fio->handle) - start_pos;
+	fio->io->seek_proc(fio->handle, start_pos, SEEK_SET);
+	return (OPJ_UINT64)file_length;
+}
+
+static OPJ_SIZE_T 
+_ReadProc(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) {
+	J2KFIO_t *fio = (J2KFIO_t*)p_user_data;
+	OPJ_SIZE_T l_nb_read = fio->io->read_proc(p_buffer, 1, (unsigned)p_nb_bytes, fio->handle);
+	return l_nb_read ? l_nb_read : (OPJ_SIZE_T)-1;
+}
+
+static OPJ_SIZE_T 
+_WriteProc(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) {
+	J2KFIO_t *fio = (J2KFIO_t*)p_user_data;  
+	return fio->io->write_proc(p_buffer, 1, (unsigned)p_nb_bytes, fio->handle);
+}
+
+static OPJ_OFF_T 
+_SkipProc(OPJ_OFF_T p_nb_bytes, void *p_user_data) {
+	J2KFIO_t *fio = (J2KFIO_t*)p_user_data;
+	if( fio->io->seek_proc(fio->handle, (long)p_nb_bytes, SEEK_CUR) ) {
+		return -1;
+	}
+	return p_nb_bytes;
+}
+
+static OPJ_BOOL 
+_SeekProc(OPJ_OFF_T p_nb_bytes, FILE * p_user_data) {
+	J2KFIO_t *fio = (J2KFIO_t*)p_user_data;
+	if( fio->io->seek_proc(fio->handle, (long)p_nb_bytes, SEEK_SET) ) {
+		return OPJ_FALSE;
+	}
+	return OPJ_TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+J2KFIO_t* 
+opj_freeimage_stream_create(FreeImageIO *io, fi_handle handle, BOOL bRead) {
+	if(!handle) {
+		return NULL;
+	}
+	J2KFIO_t *fio = (J2KFIO_t*)malloc(sizeof(J2KFIO_t));
+	if(fio) {
+		fio->io = io;
+		fio->handle = handle;
+
+		opj_stream_t *l_stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, bRead ? OPJ_TRUE : OPJ_FALSE);
+		if (l_stream) {
+			opj_stream_set_user_data(l_stream, fio, NULL);
+			opj_stream_set_user_data_length(l_stream, _LengthProc(fio));
+			opj_stream_set_read_function(l_stream, (opj_stream_read_fn)_ReadProc);
+			opj_stream_set_write_function(l_stream, (opj_stream_write_fn)_WriteProc);
+			opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn)_SkipProc);
+			opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn)_SeekProc);
+			fio->stream = l_stream;
+			return fio;
+		} else {
+			free(fio);
+		}
+	}
+
+	return NULL;		
+}
+
+void 
+opj_freeimage_stream_destroy(J2KFIO_t* fio) {
+	if(fio) {
+		if(fio->stream) {
+			opj_stream_destroy(fio->stream);
+		}
+		free(fio);
+	}
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Divide an integer by a power of 2 and round upwards
+@return Returns a divided by 2^b
+*/
+static int int_ceildivpow2(int a, int b) {
+	return (a + (1 << b) - 1) >> b;
+}
+
+/**
+Convert a OpenJPEG image to a FIBITMAP
+@param format_id Plugin ID
+@param image OpenJPEG image
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@return Returns the converted image if successful, returns NULL otherwise
+*/
+FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only) {
+	FIBITMAP *dib = NULL;
+
+	try {
+		// compute image width and height
+
+		//int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx);
+		int wr = image->comps[0].w;
+		int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor);
+		
+		//int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy);
+		//int hr = image->comps[0].h;
+		int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor);
+
+		// check the number of components
+
+		int numcomps = image->numcomps;
+
+		BOOL bIsValid = TRUE;
+		for(int c = 0; c < numcomps - 1; c++) {
+			if(	(image->comps[c].dx == image->comps[c+1].dx) && 
+				(image->comps[c].dy == image->comps[c+1].dy) &&
+				(image->comps[c].prec == image->comps[c+1].prec) ) {
+				continue;
+			} else {
+				bIsValid = FALSE;
+				break;
+			}
+		}
+		bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4));
+		if(!bIsValid) {
+			if(numcomps) {
+				FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps);
+				numcomps = 1;
+			} else {
+				// unknown type
+				throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+			}
+		}
+
+		// create a new DIB
+
+		if(image->comps[0].prec <= 8) {
+			switch(numcomps) {
+				case 1:
+					dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 8);
+					break;
+				case 3:
+					dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					break;
+				case 4:
+					dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+					break;
+			}
+		} else if(image->comps[0].prec <= 16) {
+			switch(numcomps) {
+				case 1:
+					dib = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, wrr, hrr);
+					break;
+				case 3:
+					dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, wrr, hrr);
+					break;
+				case 4:
+					dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBA16, wrr, hrr);
+					break;
+			}
+		} else {
+			throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+		}
+		if(!dib) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// "header only" FIBITMAP ?
+		if(header_only) {
+			return dib;
+		}
+		
+		if(image->comps[0].prec <= 8) {
+			if(numcomps == 1) {
+				// 8-bit greyscale
+				// ----------------------------------------------------------
+
+				// build a greyscale palette
+				
+				RGBQUAD *pal = FreeImage_GetPalette(dib);
+				for (int i = 0; i < 256; i++) {
+					pal[i].rgbRed	= (BYTE)i;
+					pal[i].rgbGreen = (BYTE)i;
+					pal[i].rgbBlue	= (BYTE)i;
+				}
+
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int index = image->comps[0].data[pixel_pos];
+						index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+						bits[x] = (BYTE)index;
+
+						pixel_count++;
+					}
+				}
+			}
+			else if(numcomps == 3) {
+
+				// 24-bit RGB
+				// ----------------------------------------------------------	
+				
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int r = image->comps[0].data[pixel_pos];
+						r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+						
+						int g = image->comps[1].data[pixel_pos];
+						g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+						
+						int b = image->comps[2].data[pixel_pos];
+						b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+						bits[FI_RGBA_RED]   = (BYTE)r;
+						bits[FI_RGBA_GREEN] = (BYTE)g;
+						bits[FI_RGBA_BLUE]  = (BYTE)b;
+						bits += 3;
+
+						pixel_count++;
+					}
+				}
+			}
+			else if(numcomps == 4) {
+
+				// 32-bit RGBA
+				// ----------------------------------------------------------	
+				
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int r = image->comps[0].data[pixel_pos];
+						r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+						
+						int g = image->comps[1].data[pixel_pos];
+						g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+						
+						int b = image->comps[2].data[pixel_pos];
+						b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+						int a = image->comps[3].data[pixel_pos];
+						a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0);
+
+						bits[FI_RGBA_RED]   = (BYTE)r;
+						bits[FI_RGBA_GREEN] = (BYTE)g;
+						bits[FI_RGBA_BLUE]  = (BYTE)b;
+						bits[FI_RGBA_ALPHA] = (BYTE)a;
+						bits += 4;
+
+						pixel_count++;
+					}
+				}
+			}
+		}
+		else if(image->comps[0].prec <= 16) {
+			if(numcomps == 1) {
+				// 16-bit greyscale
+				// ----------------------------------------------------------
+
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					WORD *bits = (WORD*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int index = image->comps[0].data[pixel_pos];
+						index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+						bits[x] = (WORD)index;
+
+						pixel_count++;
+					}
+				}
+			}
+			else if(numcomps == 3) {
+
+				// 48-bit RGB
+				// ----------------------------------------------------------	
+				
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int r = image->comps[0].data[pixel_pos];
+						r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+						
+						int g = image->comps[1].data[pixel_pos];
+						g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+						
+						int b = image->comps[2].data[pixel_pos];
+						b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+						bits[x].red   = (WORD)r;
+						bits[x].green = (WORD)g;
+						bits[x].blue  = (WORD)b;
+
+						pixel_count++;
+					}
+				}
+			}
+			else if(numcomps == 4) {
+
+				// 64-bit RGBA
+				// ----------------------------------------------------------	
+				
+				// load pixel data
+
+				unsigned pixel_count = 0;
+
+				for(int y = 0; y < hrr; y++) {		
+					FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+					for(int x = 0; x < wrr; x++) {
+						const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+						int r = image->comps[0].data[pixel_pos];
+						r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+						
+						int g = image->comps[1].data[pixel_pos];
+						g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+						
+						int b = image->comps[2].data[pixel_pos];
+						b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+						int a = image->comps[3].data[pixel_pos];
+						a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0);
+
+						bits[x].red   = (WORD)r;
+						bits[x].green = (WORD)g;
+						bits[x].blue  = (WORD)b;
+						bits[x].alpha = (WORD)a;
+
+						pixel_count++;
+					}
+				}
+			}
+		}
+
+		return dib;
+
+	} catch(const char *text) {
+		if(dib) FreeImage_Unload(dib);
+		FreeImage_OutputMessageProc(format_id, text);
+		return NULL;
+	}
+
+}
+
+/**
+Convert a FIBITMAP to a OpenJPEG image
+@param format_id Plugin ID
+@param dib FreeImage image
+@param parameters Compression parameters
+@return Returns the converted image if successful, returns NULL otherwise
+*/
+opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters) {
+	int prec, numcomps, x, y, index;
+	OPJ_COLOR_SPACE color_space;
+	opj_image_cmptparm_t cmptparm[4];	// maximum of 4 components 
+	opj_image_t *image = NULL;			// image to encode
+
+	try {
+		int w = FreeImage_GetWidth(dib);
+		int h = FreeImage_GetHeight(dib);
+
+		// get image characteristics
+		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+		if(image_type == FIT_BITMAP) {
+			// standard image ...
+			prec = 8;
+			switch(FreeImage_GetColorType(dib)) {
+				case FIC_MINISBLACK:
+					numcomps = 1;
+					color_space = OPJ_CLRSPC_GRAY;
+					break;
+				case FIC_RGB:
+					if(FreeImage_GetBPP(dib) == 32) {
+						// 32-bit image with a fully opaque layer
+						numcomps = 4;
+						color_space = OPJ_CLRSPC_SRGB;
+					} else {
+						// 24-bit image
+						numcomps = 3;
+						color_space = OPJ_CLRSPC_SRGB;
+					}
+					break;
+				case FIC_RGBALPHA:
+					numcomps = 4;
+					color_space = OPJ_CLRSPC_SRGB;
+					break;
+				default:
+					return NULL;
+			}
+		} else {
+			// HDR image ...
+			prec = 16;
+			switch(image_type) {
+				case FIT_UINT16:
+					numcomps = 1;
+					color_space = OPJ_CLRSPC_GRAY;
+					break;
+				case FIT_RGB16:
+					numcomps = 3;
+					color_space = OPJ_CLRSPC_SRGB;
+					break;
+				case FIT_RGBA16:
+					numcomps = 4;
+					color_space = OPJ_CLRSPC_SRGB;
+					break;
+				default:
+					return NULL;
+			}
+		}
+
+		// initialize image components 
+		memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t));
+		for(int i = 0; i < numcomps; i++) {
+			cmptparm[i].dx = parameters->subsampling_dx;
+			cmptparm[i].dy = parameters->subsampling_dy;
+			cmptparm[i].w = w;
+			cmptparm[i].h = h;
+			cmptparm[i].prec = prec;
+			cmptparm[i].bpp = prec;
+			cmptparm[i].sgnd = 0;
+		}
+		// create the image 
+		image = opj_image_create(numcomps, &cmptparm[0], color_space);
+		if(!image) {
+			throw FI_MSG_ERROR_DIB_MEMORY;
+		}
+
+		// set image offset and reference grid 
+		image->x0 = parameters->image_offset_x0;
+		image->y0 = parameters->image_offset_y0;
+		image->x1 = parameters->image_offset_x0 + (w - 1) *	parameters->subsampling_dx + 1;
+		image->y1 = parameters->image_offset_y0 + (h - 1) *	parameters->subsampling_dy + 1;
+
+		// set image data 
+		if(prec == 8) {
+			switch(numcomps) {
+				case 1:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[x];
+							index++;
+						}
+					}
+					break;
+				case 3:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[FI_RGBA_RED];
+							image->comps[1].data[index] = bits[FI_RGBA_GREEN];
+							image->comps[2].data[index] = bits[FI_RGBA_BLUE];
+							bits += 3;
+							index++;
+						}
+					}
+					break;
+				case 4:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[FI_RGBA_RED];
+							image->comps[1].data[index] = bits[FI_RGBA_GREEN];
+							image->comps[2].data[index] = bits[FI_RGBA_BLUE];
+							image->comps[3].data[index] = bits[FI_RGBA_ALPHA];
+							bits += 4;
+							index++;
+						}
+					}
+					break;
+			}
+		}
+		else if(prec == 16) {
+			switch(numcomps) {
+				case 1:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						WORD *bits = (WORD*)FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[x];
+							index++;
+						}
+					}
+					break;
+				case 3:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[x].red;
+							image->comps[1].data[index] = bits[x].green;
+							image->comps[2].data[index] = bits[x].blue;
+							index++;
+						}
+					}
+					break;
+				case 4:
+					index = 0;
+					for(y = 0; y < h; y++) {
+						FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, h - 1 - y);
+						for(x = 0; x < w; x++) {
+							image->comps[0].data[index] = bits[x].red;
+							image->comps[1].data[index] = bits[x].green;
+							image->comps[2].data[index] = bits[x].blue;
+							image->comps[3].data[index] = bits[x].alpha;
+							index++;
+						}
+					}
+					break;
+			}
+		}
+
+		return image;
+
+	} catch (const char *text) {
+		if(image) opj_image_destroy(image);
+		FreeImage_OutputMessageProc(format_id, text);
+		return NULL;
+	}
+}
diff --git a/files/Source/FreeImage/J2KHelper.h b/files/Source/FreeImage/J2KHelper.h
new file mode 100644
index 0000000..a8121f1
--- /dev/null
+++ b/files/Source/FreeImage/J2KHelper.h
@@ -0,0 +1,36 @@
+#ifndef J2K_HELPER_H
+#define J2K_HELPER_H
+
+// ==========================================================
+// Helper functions (see J2KHelper.cpp)
+// ==========================================================
+
+/** 
+FreeImageIO wrapper
+*/
+typedef struct tagJ2KFIO_t {
+	FreeImageIO *io;		//! FreeImage IO
+    fi_handle handle;		//! FreeImage handle
+	opj_stream_t *stream;	//! OpenJPEG stream
+} J2KFIO_t;
+
+/**
+Stream constructor
+*/
+J2KFIO_t* opj_freeimage_stream_create(FreeImageIO *io, fi_handle handle, BOOL bRead);
+
+/**
+Stream destructor
+*/
+void opj_freeimage_stream_destroy(J2KFIO_t* fio);
+
+/**
+Conversion opj_image_t => FIBITMAP
+*/
+FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only);
+/**
+Conversion FIBITMAP => opj_image_t
+*/
+opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters);
+
+#endif // J2K_HELPER_H
\ No newline at end of file
diff --git a/files/Source/FreeImage/LFPQuantizer.cpp b/files/Source/FreeImage/LFPQuantizer.cpp
new file mode 100644
index 0000000..8b592c3
--- /dev/null
+++ b/files/Source/FreeImage/LFPQuantizer.cpp
@@ -0,0 +1,208 @@
+// ==========================================================
+// LFPQuantizer class implementation
+//
+// Design and implementation by
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "Quantizers.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+LFPQuantizer::LFPQuantizer(unsigned PaletteSize) :
+		m_size(0), m_limit(PaletteSize), m_index(0) {
+	m_map = new MapEntry[MAP_SIZE];
+	memset(m_map, 0xFF, MAP_SIZE * sizeof(MapEntry));
+}
+
+LFPQuantizer::~LFPQuantizer() {
+    delete[] m_map;
+}
+
+FIBITMAP* LFPQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette) {
+
+	if (ReserveSize > 0 && ReservePalette != NULL) {
+		AddReservePalette(ReservePalette, ReserveSize);
+	}
+
+	const unsigned width = FreeImage_GetWidth(dib);
+	const unsigned height = FreeImage_GetHeight(dib);
+
+	FIBITMAP *dib8 = FreeImage_Allocate(width, height, 8);
+	if (dib8 == NULL) {
+		return NULL;
+	}
+
+	const unsigned src_pitch = FreeImage_GetPitch(dib);
+	const unsigned dst_pitch = FreeImage_GetPitch(dib8);
+
+	const BYTE * const src_bits = FreeImage_GetBits(dib);
+	BYTE * const dst_bits = FreeImage_GetBits(dib8);
+
+	unsigned last_color = -1;
+	int last_index = 0;
+
+	if (FreeImage_GetBPP(dib) == 24) {
+
+		// Getting the source pixel as an unsigned int is much faster than
+		// working with FI_RGBA_xxx and shifting. However, this may fail
+		// for the very last pixel, since its rgbReserved member (alpha)
+		// may actually point to an address beyond the bitmap's memory. So,
+		// we do not process the last scanline in the first loop.
+
+		// Process all but the last scanline.
+		for (unsigned y = 0; y < height - 1; ++y) {
+			BYTE *dst_line = dst_bits + y * dst_pitch;
+			const BYTE *src_line = src_bits + y * src_pitch;
+			for (unsigned x = 0; x < width; ++x) {
+				const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+				if (color != last_color) {
+					last_color = color;
+					last_index = GetIndexForColor(color);
+					if (last_index == -1) {
+						FreeImage_Unload(dib8);
+						return NULL;
+					}
+				}
+				dst_line[x] = last_index;
+				src_line += 3;
+			}
+		}
+
+		// Process all but the last pixel of the last scanline.
+		BYTE *dst_line = dst_bits + (height - 1) * dst_pitch;
+		const BYTE *src_line = src_bits + (height - 1) * src_pitch;
+		for (unsigned x = 0; x < width - 1; ++x) {
+			const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+			if (color != last_color) {
+				last_color = color;
+				last_index = GetIndexForColor(color);
+				if (last_index == -1) {
+					FreeImage_Unload(dib8);
+					return NULL;
+				}
+			}
+			dst_line[x] = last_index;
+			src_line += 3;
+		}
+
+		// Process the last pixel (src_line should already point to it).
+		const unsigned color = 0 | src_line[FI_RGBA_BLUE] << FI_RGBA_BLUE_SHIFT
+				| src_line[FI_RGBA_GREEN] << FI_RGBA_GREEN_SHIFT
+				| src_line[FI_RGBA_RED] << FI_RGBA_RED_SHIFT;
+		if (color != last_color) {
+			last_color = color;
+			last_index = GetIndexForColor(color);
+			if (last_index == -1) {
+				FreeImage_Unload(dib8);
+				return NULL;
+			}
+		}
+		dst_line[width - 1] = last_index;
+
+	} else {
+		for (unsigned y = 0; y < height; ++y) {
+			BYTE *dst_line = dst_bits + y * dst_pitch;
+			const BYTE *src_line = src_bits + y * src_pitch;
+			for (unsigned x = 0; x < width; ++x) {
+				const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+				if (color != last_color) {
+					last_color = color;
+					last_index = GetIndexForColor(color);
+					if (last_index == -1) {
+						FreeImage_Unload(dib8);
+						return NULL;
+					}
+				}
+				dst_line[x] = last_index;
+				src_line += 4;
+			}
+		}
+	}
+
+	WritePalette(FreeImage_GetPalette(dib8));
+	return dib8;
+}
+
+/**
+ * Returns the palette index of the specified color. Tries to put the
+ * color into the map, if it's not already present in the map. In that
+ * case, a new index is used for the color. Returns -1, if adding the
+ * color would exceed the desired maximum number of colors in the
+ * palette.
+ * @param color the color to get the index from
+ * @return the palette index of the specified color or -1, if there
+ * is no space left in the palette
+ */
+inline int LFPQuantizer::GetIndexForColor(unsigned color) {
+	unsigned bucket = hash(color) & (MAP_SIZE - 1);
+	while (m_map[bucket].color != color) {
+		if (m_map[bucket].color == EMPTY_BUCKET) {
+			if (m_size == m_limit) {
+				return -1;
+			}
+			m_map[bucket].color = color;
+			m_map[bucket].index = m_index++;
+			++m_size;
+			break;
+		}
+		bucket = (bucket + 1) % MAP_SIZE;
+	}
+	return m_map[bucket].index;
+}
+
+/**
+ * Adds the specified number of entries of the specified reserve
+ * palette to the newly created palette.
+ * @param *palette a pointer to the reserve palette to copy from
+ * @param size the number of entries to copy
+ */
+void LFPQuantizer::AddReservePalette(const void *palette, unsigned size) {
+	if (size > MAX_SIZE) {
+		size = MAX_SIZE;
+	}
+	unsigned *ppal = (unsigned *) palette;
+	const unsigned offset = m_limit - size;
+	for (unsigned i = 0; i < size; ++i) {
+		const unsigned color = *ppal++;
+		const unsigned index = i + offset;
+		unsigned bucket = hash(color) & (MAP_SIZE - 1);
+		while((m_map[bucket].color != EMPTY_BUCKET) && (m_map[bucket].color != color)) {
+			bucket = (bucket + 1) % MAP_SIZE;
+		}
+		if(m_map[bucket].color != color) {
+			m_map[bucket].color = color;
+			m_map[bucket].index = index;
+		}
+	}
+	m_size += size;
+}
+
+/**
+ * Copies the newly created palette into the specified destination
+ * palette. Although unused palette entries are not overwritten in
+ * the destination palette, it is assumed to have space for at
+ * least 256 entries.
+ * @param palette a pointer to the destination palette
+ */
+void LFPQuantizer::WritePalette(void *palette) {
+	for (unsigned i = 0; i < MAP_SIZE; ++i) {
+		if (m_map[i].color != EMPTY_BUCKET) {
+			((unsigned *) palette)[m_map[i].index] = m_map[i].color;
+		}
+	}
+}
diff --git a/files/Source/FreeImage/MNGHelper.cpp b/files/Source/FreeImage/MNGHelper.cpp
new file mode 100644
index 0000000..ed3664c
--- /dev/null
+++ b/files/Source/FreeImage/MNGHelper.cpp
@@ -0,0 +1,1320 @@
+// ==========================================================
+// MNG / JNG helpers
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+/**
+References
+http://www.libpng.org/pub/mng/spec/jng.html
+http://www.w3.org/TR/PNG/
+http://libpng.org/pub/mng/spec/
+*/
+
+// --------------------------------------------------------------------------
+
+#define MNG_INCLUDE_JNG
+
+#ifdef MNG_INCLUDE_JNG
+#define MNG_COLORTYPE_JPEGGRAY           8       /* JHDR */
+#define MNG_COLORTYPE_JPEGCOLOR         10
+#define MNG_COLORTYPE_JPEGGRAYA         12
+#define MNG_COLORTYPE_JPEGCOLORA        14
+
+#define MNG_BITDEPTH_JPEG8               8       /* JHDR */
+#define MNG_BITDEPTH_JPEG12             12
+#define MNG_BITDEPTH_JPEG8AND12         20
+
+#define MNG_COMPRESSION_BASELINEJPEG     8       /* JHDR */
+
+#define MNG_INTERLACE_SEQUENTIAL         0       /* JHDR */
+#define MNG_INTERLACE_PROGRESSIVE        8
+#endif /* MNG_INCLUDE_JNG */
+
+// --------------------------------------------------------------------------
+
+#define JNG_SUPPORTED
+
+/** Size of a JDAT chunk on writing */
+const DWORD JPEG_CHUNK_SIZE	= 8192;
+
+/** PNG signature */
+static const BYTE g_png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+/** JNG signature */
+static const BYTE g_jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 };
+
+// --------------------------------------------------------------------------
+
+/** Chunk type converted to enum */
+enum eChunckType {
+	UNKNOWN_CHUNCK,
+	MHDR,
+	BACK,
+	BASI,
+	CLIP,
+	CLON,
+	DEFI,
+	DHDR,
+	DISC,
+	ENDL,
+	FRAM,
+	IEND,
+	IHDR,
+	JHDR,
+	LOOP,
+	MAGN,
+	MEND,
+	MOVE,
+	PAST,
+	PLTE,
+	SAVE,
+	SEEK,
+	SHOW,
+	TERM,
+	bKGD,
+	cHRM,
+	gAMA,
+	iCCP,
+	nEED,
+	pHYg,
+	vpAg,
+	pHYs,
+	sBIT,
+	sRGB,
+	tRNS,
+	IDAT,
+	JDAT,
+	JDAA,
+	JdAA,
+	JSEP,
+	oFFs,
+	hIST,
+	iTXt,
+	sPLT,
+	sTER,
+	tEXt,
+	tIME,
+	zTXt
+};
+
+/**
+Helper for map<key, value> where value is a pointer to a string. 
+Used to store tEXt metadata. 
+*/
+typedef std::map<std::string, std::string> tEXtMAP;
+
+// --------------------------------------------------------------------------
+
+/*
+  Constant strings for known chunk types.  If you need to add a chunk,
+  add a string holding the name here.   To make the code more
+  portable, we use ASCII numbers like this, not characters.
+*/
+
+static BYTE mng_MHDR[5]={ 77,  72,  68,  82, (BYTE) '\0'};
+static BYTE mng_BACK[5]={ 66,  65,  67,  75, (BYTE) '\0'};
+static BYTE mng_BASI[5]={ 66,  65,  83,  73, (BYTE) '\0'};
+static BYTE mng_CLIP[5]={ 67,  76,  73,  80, (BYTE) '\0'};
+static BYTE mng_CLON[5]={ 67,  76,  79,  78, (BYTE) '\0'};
+static BYTE mng_DEFI[5]={ 68,  69,  70,  73, (BYTE) '\0'};
+static BYTE mng_DHDR[5]={ 68,  72,  68,  82, (BYTE) '\0'};
+static BYTE mng_DISC[5]={ 68,  73,  83,  67, (BYTE) '\0'};
+static BYTE mng_ENDL[5]={ 69,  78,  68,  76, (BYTE) '\0'};
+static BYTE mng_FRAM[5]={ 70,  82,  65,  77, (BYTE) '\0'};
+static BYTE mng_IEND[5]={ 73,  69,  78,  68, (BYTE) '\0'};
+static BYTE mng_IHDR[5]={ 73,  72,  68,  82, (BYTE) '\0'};
+static BYTE mng_JHDR[5]={ 74,  72,  68,  82, (BYTE) '\0'};
+static BYTE mng_LOOP[5]={ 76,  79,  79,  80, (BYTE) '\0'};
+static BYTE mng_MAGN[5]={ 77,  65,  71,  78, (BYTE) '\0'};
+static BYTE mng_MEND[5]={ 77,  69,  78,  68, (BYTE) '\0'};
+static BYTE mng_MOVE[5]={ 77,  79,  86,  69, (BYTE) '\0'};
+static BYTE mng_PAST[5]={ 80,  65,  83,  84, (BYTE) '\0'};
+static BYTE mng_PLTE[5]={ 80,  76,  84,  69, (BYTE) '\0'};
+static BYTE mng_SAVE[5]={ 83,  65,  86,  69, (BYTE) '\0'};
+static BYTE mng_SEEK[5]={ 83,  69,  69,  75, (BYTE) '\0'};
+static BYTE mng_SHOW[5]={ 83,  72,  79,  87, (BYTE) '\0'};
+static BYTE mng_TERM[5]={ 84,  69,  82,  77, (BYTE) '\0'};
+static BYTE mng_bKGD[5]={ 98,  75,  71,  68, (BYTE) '\0'};
+static BYTE mng_cHRM[5]={ 99,  72,  82,  77, (BYTE) '\0'};
+static BYTE mng_gAMA[5]={103,  65,  77,  65, (BYTE) '\0'};
+static BYTE mng_iCCP[5]={105,  67,  67,  80, (BYTE) '\0'};
+static BYTE mng_nEED[5]={110,  69,  69,  68, (BYTE) '\0'};
+static BYTE mng_pHYg[5]={112,  72,  89, 103, (BYTE) '\0'};
+static BYTE mng_vpAg[5]={118, 112,  65, 103, (BYTE) '\0'};
+static BYTE mng_pHYs[5]={112,  72,  89, 115, (BYTE) '\0'};
+static BYTE mng_sBIT[5]={115,  66,  73,  84, (BYTE) '\0'};
+static BYTE mng_sRGB[5]={115,  82,  71,  66, (BYTE) '\0'};
+static BYTE mng_tRNS[5]={116,  82,  78,  83, (BYTE) '\0'};
+
+#if defined(JNG_SUPPORTED)
+static BYTE mng_IDAT[5]={ 73,  68,  65,  84, (BYTE) '\0'};
+static BYTE mng_JDAT[5]={ 74,  68,  65,  84, (BYTE) '\0'};
+static BYTE mng_JDAA[5]={ 74,  68,  65,  65, (BYTE) '\0'};
+static BYTE mng_JdAA[5]={ 74, 100,  65,  65, (BYTE) '\0'};
+static BYTE mng_JSEP[5]={ 74,  83,  69,  80, (BYTE) '\0'};
+static BYTE mng_oFFs[5]={111,  70,  70, 115, (BYTE) '\0'};
+#endif
+
+static BYTE mng_hIST[5]={104,  73,  83,  84, (BYTE) '\0'};
+static BYTE mng_iTXt[5]={105,  84,  88, 116, (BYTE) '\0'};
+static BYTE mng_sPLT[5]={115,  80,  76,  84, (BYTE) '\0'};
+static BYTE mng_sTER[5]={115,  84,  69,  82, (BYTE) '\0'};
+static BYTE mng_tEXt[5]={116,  69,  88, 116, (BYTE) '\0'};
+static BYTE mng_tIME[5]={116,  73,  77,  69, (BYTE) '\0'};
+static BYTE mng_zTXt[5]={122,  84,  88, 116, (BYTE) '\0'};
+
+
+// --------------------------------------------------------------------------
+
+/**
+Convert a chunk name to a unique ID
+*/
+static eChunckType 
+mng_GetChunckType(const BYTE *mChunkName) {
+	if(memcmp(mChunkName, mng_MHDR, 4) == 0) {
+		return MHDR;
+	}
+	if(memcmp(mChunkName, mng_LOOP, 4) == 0) {
+		return LOOP;
+	}
+	if(memcmp(mChunkName, mng_DEFI, 4) == 0) {
+		return DEFI;
+	}
+	if(memcmp(mChunkName, mng_PLTE, 4) == 0) {
+		return PLTE;
+	}
+	if(memcmp(mChunkName, mng_tRNS, 4) == 0) {
+		return tRNS;
+	}
+	if(memcmp(mChunkName, mng_IHDR, 4) == 0) {
+		return IHDR;
+	}
+	if(memcmp(mChunkName, mng_JHDR, 4) == 0) {
+		return JHDR;
+	}
+	if(memcmp(mChunkName, mng_MEND, 4) == 0) {
+		return MEND;
+	}
+	if(memcmp(mChunkName, mng_IEND, 4) == 0) {
+		return IEND;
+	}
+	if(memcmp(mChunkName, mng_JDAT, 4) == 0) {
+		return JDAT;
+	}
+	if(memcmp(mChunkName, mng_IDAT, 4) == 0) {
+		return IDAT;
+	}
+	if(memcmp(mChunkName, mng_JDAA, 4) == 0) {
+		return JDAA;
+	}
+	if(memcmp(mChunkName, mng_gAMA, 4) == 0) {
+		return gAMA;
+	}
+	if(memcmp(mChunkName, mng_pHYs, 4) == 0) {
+		return pHYs;
+	}
+	if(memcmp(mChunkName, mng_bKGD, 4) == 0) {
+		return bKGD;
+	}
+	if(memcmp(mChunkName, mng_tEXt, 4) == 0) {
+		return tEXt;
+	}
+
+	return UNKNOWN_CHUNCK;
+}
+
+inline void
+mng_SwapShort(WORD *sp) {
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapShort(sp);
+#endif
+}
+
+inline void
+mng_SwapLong(DWORD *lp) {
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapLong(lp);
+#endif
+}
+
+/**
+Returns the size, in bytes, of a FreeImageIO stream, from the current position. 
+*/
+static long
+mng_LOF(FreeImageIO *io, fi_handle handle) {
+	long start_pos = io->tell_proc(handle);
+	io->seek_proc(handle, 0, SEEK_END);
+	long file_length = io->tell_proc(handle);
+	io->seek_proc(handle, start_pos, SEEK_SET);
+	return file_length;
+}
+
+/**
+Count the number of bytes in a PNG stream, from IHDR to IEND. 
+If successful, the stream position, as given by io->tell_proc(handle), 
+should be the end of the PNG stream at the return of the function. 
+@param io
+@param handle
+@param inPos
+@param m_TotalBytesOfChunks
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+mng_CountPNGChunks(FreeImageIO *io, fi_handle handle, long inPos, unsigned *m_TotalBytesOfChunks) {
+	long mLOF;
+	long mPos;
+	BOOL mEnd = FALSE;
+	DWORD mLength = 0;
+	BYTE mChunkName[5];
+
+	*m_TotalBytesOfChunks = 0;
+
+	// get the length of the file
+	mLOF = mng_LOF(io, handle);
+
+	// go to the start of the file
+	io->seek_proc(handle, inPos, SEEK_SET);
+
+	try {
+		// parse chunks
+		while(mEnd == FALSE) {
+			// chunk length
+			mPos = io->tell_proc(handle);
+			if(mPos + 4 > mLOF) {
+				throw(1);
+			}
+			io->read_proc(&mLength, 1, 4, handle);
+			mng_SwapLong(&mLength);
+			// chunk name
+			mPos = io->tell_proc(handle);
+			if(mPos + 4 > mLOF) {
+				throw(1);
+			}
+			io->read_proc(&mChunkName[0], 1, 4, handle);
+			mChunkName[4] = '\0';
+
+			// go to next chunk
+			mPos = io->tell_proc(handle);
+			// 4 = size of the CRC
+			if(mPos + (long)mLength + 4 > mLOF) {
+				throw(1);
+			}
+			io->seek_proc(handle, mLength + 4, SEEK_CUR);
+
+			switch( mng_GetChunckType(mChunkName) ) {
+				case IHDR:
+					if(mLength != 13) {
+						throw(1);
+					}
+					break;
+				
+				case IEND:
+					mEnd = TRUE;		
+					// the length below includes 4 bytes CRC, but no bytes for Length
+					*m_TotalBytesOfChunks = io->tell_proc(handle) - inPos;
+					break;		
+				
+				case UNKNOWN_CHUNCK:
+				default:
+					break;
+			}
+
+		} // while(!mEnd)
+
+		return TRUE;
+		
+	} catch(int) {
+		return FALSE;
+	}
+}
+
+/**
+Retrieve the position of a chunk in a PNG stream
+@param hPngMemory PNG stream handle
+@param chunk_name Name of the chunk to be found
+@param offset Start of the search in the stream
+@param start_pos [returned value] Start position of the chunk
+@param next_pos [returned value] Start position of the next chunk
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+mng_FindChunk(FIMEMORY *hPngMemory, BYTE *chunk_name, long offset, DWORD *start_pos, DWORD *next_pos) {
+	DWORD mLength = 0;
+
+	BYTE *data = NULL;
+	DWORD size_in_bytes = 0;
+
+	*start_pos = 0;
+	*next_pos = 0;
+
+	// get a pointer to the stream buffer
+	FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+	if(!(data && size_in_bytes) || (size_in_bytes < 20) || (size_in_bytes - offset < 20)) {
+		// not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+		return FALSE;
+	}
+
+	try {
+	
+		// skip the signature and/or any following chunk(s)
+		DWORD chunk_pos = offset;
+
+		while(1) {
+			// get chunk length
+			if(chunk_pos + 4 > size_in_bytes) {
+				break;
+			}
+
+			memcpy(&mLength, &data[chunk_pos], 4);
+			mng_SwapLong(&mLength);
+			chunk_pos += 4;
+
+			const DWORD next_chunk_pos = chunk_pos + 4 + mLength + 4;
+			if(next_chunk_pos > size_in_bytes) {
+				break;
+			}
+
+			// get chunk name
+			if(memcmp(&data[chunk_pos], chunk_name, 4) == 0) {
+				chunk_pos -= 4;	// found chunk
+				*start_pos = chunk_pos;
+				*next_pos = next_chunk_pos;
+				return TRUE;
+			}
+			
+			chunk_pos = next_chunk_pos;
+		}
+
+		return FALSE;
+
+	} catch(int) {
+		return FALSE;
+	}
+}
+
+/**
+Remove a chunk located at (start_pos, next_pos) in the PNG stream
+@param hPngMemory PNG stream handle
+@param start_pos Start position of the chunk
+@param next_pos Start position of the next chunk
+@return Returns TRUE if successfull, returns FALSE otherwise
+*/
+static BOOL 
+mng_CopyRemoveChunks(FIMEMORY *hPngMemory, DWORD start_pos, DWORD next_pos) {
+	BYTE *data = NULL;
+	DWORD size_in_bytes = 0;
+
+	// length of the chunk to remove
+	DWORD chunk_length = next_pos - start_pos;
+	if(chunk_length == 0) {
+		return TRUE;
+	}
+
+	// get a pointer to the stream buffer
+	FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+	if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
+		// not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+		return FALSE;
+	}
+	
+	// new file length
+	unsigned buffer_size = size_in_bytes + chunk_length;
+
+	BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
+	if(!buffer) {
+		return FALSE;
+	}
+	memcpy(&buffer[0], &data[0], start_pos);
+	memcpy(&buffer[start_pos], &data[next_pos], size_in_bytes - next_pos);
+
+	// seek to the start of the stream
+	FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+	// re-write the stream
+	FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
+
+	free(buffer);
+
+	return TRUE;
+}
+
+/**
+Insert a chunk just before the inNextChunkName chunk
+@param hPngMemory PNG stream handle
+@param start_pos Start position of the inNextChunkName chunk
+@param next_pos Start position of the next chunk
+@return Returns TRUE if successfull, returns FALSE otherwise
+*/
+static BOOL 
+mng_CopyInsertChunks(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, DWORD inChunkLength, DWORD start_pos, DWORD next_pos) {
+	BYTE *data = NULL;
+	DWORD size_in_bytes = 0;
+
+	// length of the chunk to check
+	DWORD chunk_length = next_pos - start_pos;
+	if(chunk_length == 0) {
+		return TRUE;
+	}
+
+	// get a pointer to the stream buffer
+	FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+	if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
+		// not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+		return FALSE;
+	}
+	
+	// new file length
+	unsigned buffer_size = inChunkLength + size_in_bytes;
+
+	BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
+	if(!buffer) {
+		return FALSE;
+	}
+	unsigned p = 0;
+	memcpy(&buffer[p], &data[0], start_pos);
+	p += start_pos;
+	memcpy(&buffer[p], inInsertChunk, inChunkLength);
+	p += inChunkLength;
+	memcpy(&buffer[p], &data[start_pos], size_in_bytes - start_pos);
+
+	// seek to the start of the stream
+	FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+	// re-write the stream
+	FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
+
+	free(buffer);
+
+	return TRUE;
+}
+
+static BOOL 
+mng_RemoveChunk(FIMEMORY *hPngMemory, BYTE *chunk_name) {
+	BOOL bResult = FALSE;
+
+	DWORD start_pos = 0;
+	DWORD next_pos = 0;
+	
+	bResult = mng_FindChunk(hPngMemory, chunk_name, 8, &start_pos, &next_pos);
+	if(!bResult) {
+		return FALSE;
+	}
+
+	bResult = mng_CopyRemoveChunks(hPngMemory, start_pos, next_pos);
+	if(!bResult) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static BOOL 
+mng_InsertChunk(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, unsigned chunk_length) {
+	BOOL bResult = FALSE;
+
+	DWORD start_pos = 0;
+	DWORD next_pos = 0;
+	
+	bResult = mng_FindChunk(hPngMemory, inNextChunkName, 8, &start_pos, &next_pos);
+	if(!bResult) {
+		return FALSE;
+	}
+
+	bResult = mng_CopyInsertChunks(hPngMemory, inNextChunkName, inInsertChunk, chunk_length, start_pos, next_pos);
+	if(!bResult) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static FIBITMAP* 
+mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) {
+	long offset = 0;
+	FIBITMAP *dib = NULL;
+
+	if(hmem) {
+		// seek to the start of the stream
+		FreeImage_SeekMemory(hmem, offset, SEEK_SET);
+
+		// check the file signature and deduce its format
+		// (the second argument is currently not used by FreeImage)
+		FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
+		if(fif != FIF_UNKNOWN) {
+			dib = FreeImage_LoadFromMemory(fif, hmem, flags);
+		}
+	}
+	
+	return dib;
+}
+
+/**
+Write a chunk in a PNG stream from the current position. 
+@param chunk_name Name of the chunk
+@param chunk_data Chunk array
+@param length Chunk length
+@param hPngMemory PNG stream handle
+*/
+static void
+mng_WriteChunk(BYTE *chunk_name, BYTE *chunk_data, DWORD length, FIMEMORY *hPngMemory) {
+	DWORD crc_file = 0;
+	// write a PNG chunk ...
+	// - length
+	mng_SwapLong(&length);
+	FreeImage_WriteMemory(&length, 1, 4, hPngMemory);
+	mng_SwapLong(&length);
+	// - chunk name
+	FreeImage_WriteMemory(chunk_name, 1, 4, hPngMemory);
+	if(chunk_data && length) {
+		// - chunk data
+		FreeImage_WriteMemory(chunk_data, 1, length, hPngMemory);
+		// - crc
+		crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
+		crc_file = FreeImage_ZLibCRC32(crc_file, chunk_data, length);
+		mng_SwapLong(&crc_file);
+		FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
+	} else {
+		// - crc
+		crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
+		mng_SwapLong(&crc_file);
+		FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
+	}
+
+}
+
+/**
+Wrap a IDAT chunk as a PNG stream. 
+The stream has the structure { g_png_signature, IHDR, IDAT, IEND }
+The image is assumed to be a greyscale image. 
+
+@param jng_width Image width
+@param jng_height Image height
+@param jng_alpha_sample_depth Bits per pixel
+@param mChunk PNG grayscale IDAT format
+@param mLength IDAT chunk length
+@param hPngMemory Output memory stream
+*/
+static void 
+mng_WritePNGStream(DWORD jng_width, DWORD jng_height, BYTE jng_alpha_sample_depth, BYTE *mChunk, DWORD mLength, FIMEMORY *hPngMemory) {
+	// PNG grayscale IDAT format
+
+	BYTE data[14];
+
+	// wrap the IDAT chunk as a PNG stream
+
+	// write PNG file signature
+	FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
+
+	// write a IHDR chunk ...
+	/*
+	The IHDR chunk must appear FIRST. It contains:
+	Width:              4 bytes
+	Height:             4 bytes
+	Bit depth:          1 byte
+	Color type:         1 byte
+	Compression method: 1 byte
+	Filter method:      1 byte
+	Interlace method:   1 byte
+	*/
+	// - chunk data
+	mng_SwapLong(&jng_width);
+	mng_SwapLong(&jng_height);
+	memcpy(&data[0], &jng_width, 4);
+	memcpy(&data[4], &jng_height, 4);
+	mng_SwapLong(&jng_width);
+	mng_SwapLong(&jng_height);
+	data[8] = jng_alpha_sample_depth;
+	data[9] = 0;	// color_type gray (jng_color_type)
+	data[10] = 0;	// compression method 0 (jng_alpha_compression_method)
+	data[11] = 0;	// filter_method 0 (jng_alpha_filter_method)
+	data[12] = 0;	// interlace_method 0 (jng_alpha_interlace_method)
+
+	mng_WriteChunk(mng_IHDR, &data[0], 13, hPngMemory);
+
+	// write a IDAT chunk ...
+	mng_WriteChunk(mng_IDAT, mChunk, mLength, hPngMemory);
+
+	// write a IEND chunk ...
+	mng_WriteChunk(mng_IEND, NULL, 0, hPngMemory);
+
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Build and set a FITAG whose type is FIDT_ASCII. 
+The tag must be destroyed by the caller using FreeImage_DeleteTag.
+@param model Metadata model to be filled
+@param dib Image to be filled
+@param key Tag key
+@param value Tag value
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
+	if(!dib || !key || !value) {
+		return FALSE;
+	}
+	// create a tag
+	FITAG *tag = FreeImage_CreateTag();
+	if(tag) {
+		BOOL bSuccess = TRUE;
+		// fill the tag
+		DWORD tag_length = (DWORD)(strlen(value) + 1);
+		bSuccess &= FreeImage_SetTagKey(tag, key);
+		bSuccess &= FreeImage_SetTagLength(tag, tag_length);
+		bSuccess &= FreeImage_SetTagCount(tag, tag_length);
+		bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII);
+		bSuccess &= FreeImage_SetTagValue(tag, value);
+		if(bSuccess) {
+			// set the tag
+			FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag);
+		}
+		FreeImage_DeleteTag(tag);
+		return bSuccess;
+	}
+
+	return FALSE;
+}
+
+/**
+Read a tEXt chunk and extract the key/value pair. 
+@param key_value_pair [returned value] Array of key/value pairs
+@param mChunk Chunk data
+@param mLength Chunk length
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+mng_SetMetadata_tEXt(tEXtMAP &key_value_pair, const BYTE *mChunk, DWORD mLength) {
+	std::string key;
+	std::string value;
+	BYTE *buffer = (BYTE*)malloc(mLength * sizeof(BYTE));
+	if(!buffer) {
+		return FALSE;
+	}
+	DWORD pos = 0;
+
+	memset(buffer, 0, mLength * sizeof(BYTE));
+
+	for(DWORD i = 0; i < mLength; i++) {
+		buffer[pos++] = mChunk[i];
+		if(mChunk[i] == '\0') {
+			if(key.size() == 0) {
+				key = (char*)buffer;
+				pos = 0;
+				memset(buffer, 0, mLength * sizeof(BYTE));
+			} else {
+				break;
+			}
+		}
+	}
+	value = (char*)buffer;
+	free(buffer);
+
+	key_value_pair[key] = value;
+
+	return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Load a FIBITMAP from a MNG or a JNG stream
+@param format_id ID of the caller
+@param io Stream i/o functions
+@param handle Stream handle
+@param Offset Start of the first chunk
+@param flags Loading flags
+@return Returns a dib if successful, returns NULL otherwise
+*/
+FIBITMAP* 
+mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) {
+	DWORD mLength = 0;
+	BYTE mChunkName[5];
+	BYTE *mChunk = NULL;
+	DWORD crc_file;
+	long LastOffset;
+	long mOrigPos;
+	BYTE *PLTE_file_chunk = NULL;	// whole PLTE chunk (lentgh, name, array, crc)
+	DWORD PLTE_file_size = 0;		// size of PLTE chunk
+
+	BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk
+	unsigned m_TotalBytesOfChunks = 0;
+	FIBITMAP *dib = NULL;
+	FIBITMAP *dib_alpha = NULL;
+
+	FIMEMORY *hJpegMemory = NULL;
+	FIMEMORY *hPngMemory = NULL;
+	FIMEMORY *hIDATMemory = NULL;
+
+	// ---
+	DWORD jng_width = 0;
+	DWORD jng_height = 0;
+	BYTE jng_color_type = 0;
+	BYTE jng_image_sample_depth = 0;
+	BYTE jng_image_compression_method = 0;
+
+	BYTE jng_alpha_sample_depth = 0;
+	BYTE jng_alpha_compression_method = 0;
+	BYTE jng_alpha_filter_method = 0;
+	BYTE jng_alpha_interlace_method = 0;
+
+	DWORD mng_frame_width = 0;
+	DWORD mng_frame_height = 0;
+	DWORD mng_ticks_per_second = 0;
+	DWORD mng_nominal_layer_count = 0;
+	DWORD mng_nominal_frame_count = 0;
+	DWORD mng_nominal_play_time = 0;
+	DWORD mng_simplicity_profile = 0;
+
+
+	DWORD res_x = 2835;	// 72 dpi
+	DWORD res_y = 2835;	// 72 dpi
+	RGBQUAD rgbBkColor = {0, 0, 0, 0};
+	WORD bk_red, bk_green, bk_blue;
+	BOOL hasBkColor = FALSE;
+	BOOL mHasIDAT = FALSE;
+
+	tEXtMAP key_value_pair;
+
+	// ---
+
+	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+	
+	// get the file size
+	const long mLOF = mng_LOF(io, handle);
+	// go to the first chunk
+	io->seek_proc(handle, Offset, SEEK_SET);
+
+	try {
+		BOOL mEnd = FALSE;
+
+		while(mEnd == FALSE) {
+			// start of the chunk
+			LastOffset = io->tell_proc(handle);
+			// read length
+			mLength = 0;			
+			io->read_proc(&mLength, 1, sizeof(mLength), handle);
+			mng_SwapLong(&mLength);
+			// read name			
+			io->read_proc(&mChunkName[0], 1, 4, handle);
+			mChunkName[4] = '\0';
+
+			if(mLength > 0) {
+				mChunk = (BYTE*)realloc(mChunk, mLength);
+				if(!mChunk) {
+					FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+					throw (const char*)NULL;
+				}				
+				Offset = io->tell_proc(handle);
+				if(Offset + (long)mLength > mLOF) {
+					FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName);
+					throw (const char*)NULL;
+				}
+				// read chunk
+				io->read_proc(mChunk, 1, mLength, handle);
+			}
+			// read crc
+			io->read_proc(&crc_file, 1, sizeof(crc_file), handle);
+			mng_SwapLong(&crc_file);
+			// check crc
+			DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4);
+			crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength);
+			if(crc_check != crc_file) {
+				FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName);
+				throw (const char*)NULL;
+			}		
+
+			switch( mng_GetChunckType(mChunkName) ) {
+				case MHDR:
+					// The MHDR chunk is always first in all MNG datastreams except for those 
+					// that consist of a single PNG or JNG datastream with a PNG or JNG signature. 
+					if(mLength == 28) {
+						memcpy(&mng_frame_width, &mChunk[0], 4);
+						memcpy(&mng_frame_height, &mChunk[4], 4);
+						memcpy(&mng_ticks_per_second, &mChunk[8], 4);
+						memcpy(&mng_nominal_layer_count, &mChunk[12], 4);
+						memcpy(&mng_nominal_frame_count, &mChunk[16], 4);
+						memcpy(&mng_nominal_play_time, &mChunk[20], 4);
+						memcpy(&mng_simplicity_profile, &mChunk[24], 4);
+
+						mng_SwapLong(&mng_frame_width);
+						mng_SwapLong(&mng_frame_height);
+						mng_SwapLong(&mng_ticks_per_second);
+						mng_SwapLong(&mng_nominal_layer_count);
+						mng_SwapLong(&mng_nominal_frame_count);
+						mng_SwapLong(&mng_nominal_play_time);
+						mng_SwapLong(&mng_simplicity_profile);
+
+					} else {
+						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength);
+					}
+					break;
+
+				case MEND:
+					mEnd = TRUE;
+					break;
+
+				case LOOP:
+				case ENDL:
+					break;
+				case DEFI:
+					break;
+				case SAVE:
+				case SEEK:
+				case TERM:
+					break;
+				case BACK:
+					break;
+
+					// Global "PLTE" and "tRNS" (if any).  PNG "PLTE" will be of 0 byte, as it uses global data.
+				case PLTE:	// Global
+					m_HasGlobalPalette = TRUE;
+					PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4)
+					PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size);
+					if(!PLTE_file_chunk) {
+						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+						throw (const char*)NULL;
+					} else {
+						mOrigPos = io->tell_proc(handle);
+						// seek to the start of the chunk
+						io->seek_proc(handle, LastOffset, SEEK_SET);
+						// load the whole chunk
+						io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle);
+						// go to the start of the next chunk
+						io->seek_proc(handle, mOrigPos, SEEK_SET);
+					}
+					break;
+
+				case tRNS:	// Global
+					break;
+					
+				case IHDR:
+					Offset = LastOffset;
+					// parse the PNG file and get its file size
+					if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) {
+						// reach an unexpected end of file
+						mEnd = TRUE;
+						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName);
+						break;
+					}
+					
+					// wrap the { IHDR, ..., IEND } chunks as a PNG stream
+					if(hPngMemory == NULL) {
+						hPngMemory = FreeImage_OpenMemory();
+					}
+
+					mOrigPos = io->tell_proc(handle);
+
+					// write PNG file signature
+					FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+					FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
+
+					mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks);
+					if(!mChunk) {
+						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+						throw (const char*)NULL;
+					}
+					
+					// on calling CountPNGChunks earlier, we were in Offset pos,
+					// go back there
+					io->seek_proc(handle, Offset, SEEK_SET);
+					io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle);
+					// Put back to original pos
+					io->seek_proc(handle, mOrigPos, SEEK_SET);
+					// write the PNG chunks
+					FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory);
+
+					// plug in global PLTE if local PLTE exists
+					if(m_HasGlobalPalette) {
+						// ensure we remove some local chunks, so that global
+						// "PLTE" can be inserted right before "IDAT".
+						mng_RemoveChunk(hPngMemory, mng_PLTE);
+						mng_RemoveChunk(hPngMemory, mng_tRNS);
+						mng_RemoveChunk(hPngMemory, mng_bKGD);
+						// insert global "PLTE" chunk in its entirety before "IDAT"
+						mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size);
+					}
+
+					if(dib) FreeImage_Unload(dib);
+					dib = mng_LoadFromMemoryHandle(hPngMemory, flags);
+
+					// stop after the first image
+					mEnd = TRUE;
+					break;
+
+				case JHDR:
+					if(mLength == 16) {
+						memcpy(&jng_width, &mChunk[0], 4);
+						memcpy(&jng_height, &mChunk[4], 4);
+						mng_SwapLong(&jng_width);
+						mng_SwapLong(&jng_height);
+
+						jng_color_type = mChunk[8];
+						jng_image_sample_depth = mChunk[9];
+						jng_image_compression_method = mChunk[10];
+						//BYTE jng_image_interlace_method = mChunk[11];	// for debug only
+
+						jng_alpha_sample_depth = mChunk[12];
+						jng_alpha_compression_method = mChunk[13];
+						jng_alpha_filter_method = mChunk[14];
+						jng_alpha_interlace_method = mChunk[15];
+					} else {
+						FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName);
+						throw (const char*)NULL;
+					}
+					break;
+
+				case JDAT:
+					if(hJpegMemory == NULL) {
+						hJpegMemory = FreeImage_OpenMemory();
+					}
+					// as there may be several JDAT chunks, concatenate them
+					FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory);
+					break;
+
+				case IDAT:
+					if(!header_only && (jng_alpha_compression_method == 0)) {
+						// PNG grayscale IDAT format
+						if(hIDATMemory == NULL) {
+							hIDATMemory = FreeImage_OpenMemory();
+							mHasIDAT = TRUE;
+						}
+						// as there may be several IDAT chunks, concatenate them
+						FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory);
+					}
+					break;
+
+				case IEND:
+					if(!hJpegMemory) {
+						mEnd = TRUE;
+						break;
+					}
+					// load the JPEG
+					if(dib) {
+						FreeImage_Unload(dib);
+					}
+					dib = mng_LoadFromMemoryHandle(hJpegMemory, flags);
+
+					// load the PNG alpha layer
+					if(mHasIDAT) {
+						BYTE *data = NULL;
+						DWORD size_in_bytes = 0;
+
+						// get a pointer to the IDAT buffer
+						FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes);
+						if(data && size_in_bytes) {
+							// wrap the IDAT chunk as a PNG stream
+							if(hPngMemory == NULL) {
+								hPngMemory = FreeImage_OpenMemory();
+							}
+							mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory);
+							// load the PNG
+							if(dib_alpha) {
+								FreeImage_Unload(dib_alpha);
+							}
+							dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags);
+						}
+					}
+					// stop the parsing
+					mEnd = TRUE;
+					break;
+
+				case JDAA:
+					break;
+
+				case gAMA:
+					break;
+
+				case pHYs:
+					// unit is pixels per meter
+					memcpy(&res_x, &mChunk[0], 4);
+					mng_SwapLong(&res_x);
+					memcpy(&res_y, &mChunk[4], 4);
+					mng_SwapLong(&res_y);
+					break;
+
+				case bKGD:
+					memcpy(&bk_red, &mChunk[0], 2);
+					mng_SwapShort(&bk_red);
+					rgbBkColor.rgbRed = (BYTE)bk_red;
+					memcpy(&bk_green, &mChunk[2], 2);
+					mng_SwapShort(&bk_green);
+					rgbBkColor.rgbGreen = (BYTE)bk_green;
+					memcpy(&bk_blue, &mChunk[4], 2);
+					mng_SwapShort(&bk_blue);
+					rgbBkColor.rgbBlue = (BYTE)bk_blue;
+					hasBkColor = TRUE;
+					break;
+				
+				case tEXt:
+					mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength);
+					break;
+
+				case UNKNOWN_CHUNCK:
+				default:
+					break;
+
+
+			} // switch( GetChunckType )
+		} // while(!mEnd)
+
+		FreeImage_CloseMemory(hJpegMemory);
+		FreeImage_CloseMemory(hPngMemory);
+		FreeImage_CloseMemory(hIDATMemory);
+		free(mChunk);
+		free(PLTE_file_chunk);
+
+		// convert to 32-bit if a transparent layer is available
+		if(!header_only && dib_alpha) {
+			FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib);
+			if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) {
+				FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA);
+			} else {
+				FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha);
+				FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA);
+				FreeImage_Unload(dst_alpha);
+			}			
+			FreeImage_Unload(dib);
+			dib = dst;
+		}
+		FreeImage_Unload(dib_alpha);
+
+		if(dib) {
+			// set metadata
+			FreeImage_SetDotsPerMeterX(dib, res_x);
+			FreeImage_SetDotsPerMeterY(dib, res_y);
+			if(hasBkColor) {
+				FreeImage_SetBackgroundColor(dib, &rgbBkColor);
+			}
+			if(key_value_pair.size()) {
+				for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) {
+					std::string key = (*j).first;
+					std::string value = (*j).second;
+					mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str());
+				}
+			}
+		}
+			
+		return dib;
+
+	} catch(const char *text) {
+		FreeImage_CloseMemory(hJpegMemory);
+		FreeImage_CloseMemory(hPngMemory);
+		FreeImage_CloseMemory(hIDATMemory);
+		free(mChunk);
+		free(PLTE_file_chunk);
+		FreeImage_Unload(dib);
+		FreeImage_Unload(dib_alpha);
+		if(text) {
+			FreeImage_OutputMessageProc(format_id, text);
+		}
+		return NULL;
+	}
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Write a FIBITMAP to a JNG stream
+@param format_id ID of the caller
+@param io Stream i/o functions
+@param dib Image to be saved
+@param handle Stream handle
+@param flags Saving flags
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL 
+mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags) {
+	DWORD jng_width = 0;
+	DWORD jng_height = 0;
+	BYTE jng_color_type = 0;
+	BYTE jng_image_sample_depth = 8;
+	BYTE jng_image_compression_method = 8;	//  8: ISO-10918-1 Huffman-coded baseline JPEG.
+	BYTE jng_image_interlace_method = 0;
+
+	BYTE jng_alpha_sample_depth = 0;
+	BYTE jng_alpha_compression_method = 0;
+	BYTE jng_alpha_filter_method = 0;
+	BYTE jng_alpha_interlace_method = 0;
+
+	BYTE buffer[16];
+
+	FIMEMORY *hJngMemory = NULL;
+	FIMEMORY *hJpegMemory = NULL;
+	FIMEMORY *hPngMemory = NULL;
+
+	FIBITMAP *dib_rgb = NULL;
+	FIBITMAP *dib_alpha = NULL;
+
+	if(!dib || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+		return FALSE;
+	}
+
+	unsigned bpp = FreeImage_GetBPP(dib);
+
+	switch(bpp) {
+		case 8:
+			if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+				dib_rgb = dib;
+				jng_color_type = MNG_COLORTYPE_JPEGGRAY;
+			} else {
+				// JPEG plugin will convert other types (FIC_MINISWHITE, FIC_PALETTE) to 24-bit on the fly
+				//dib_rgb = FreeImage_ConvertTo24Bits(dib);
+				dib_rgb = dib;
+				jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
+
+			}
+			break;
+		case 24:
+			dib_rgb = dib;
+			jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
+			break;
+		case 32:
+			dib_rgb = FreeImage_ConvertTo24Bits(dib);
+			jng_color_type = MNG_COLORTYPE_JPEGCOLORA;
+			jng_alpha_sample_depth = 8;
+			break;
+		default:
+			return FALSE;
+	}
+
+	jng_width = (DWORD)FreeImage_GetWidth(dib);
+	jng_height = (DWORD)FreeImage_GetHeight(dib);
+
+	try {
+		hJngMemory = FreeImage_OpenMemory();
+
+		// --- write JNG file signature ---
+		FreeImage_WriteMemory(g_jng_signature, 1, 8, hJngMemory);
+
+		// --- write a JHDR chunk ---
+		SwapLong(&jng_width);
+		SwapLong(&jng_height);
+		memcpy(&buffer[0], &jng_width, 4);
+		memcpy(&buffer[4], &jng_height, 4);
+		SwapLong(&jng_width);
+		SwapLong(&jng_height);
+		buffer[8] = jng_color_type;
+		buffer[9] = jng_image_sample_depth;
+		buffer[10] = jng_image_compression_method;
+		buffer[11] = jng_image_interlace_method;
+		buffer[12] = jng_alpha_sample_depth;
+		buffer[13] = jng_alpha_compression_method;
+		buffer[14] = jng_alpha_filter_method;
+		buffer[15] = jng_alpha_interlace_method;
+		mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory);
+
+		// --- write a sequence of JDAT chunks ---
+		hJpegMemory = FreeImage_OpenMemory();
+		flags |= JPEG_BASELINE;
+		if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) {
+			throw (const char*)NULL;
+		}
+		if(dib_rgb != dib) {
+			FreeImage_Unload(dib_rgb);
+			dib_rgb = NULL;
+		}
+		{
+			BYTE *jpeg_data = NULL;
+			DWORD size_in_bytes = 0;
+			
+			// get a pointer to the stream buffer
+			FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes);
+			// write chunks
+			for(DWORD k = 0; k < size_in_bytes;) {
+				DWORD bytes_left = size_in_bytes - k;
+				DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left);
+				mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory);
+				k += chunk_size;
+			}
+		}
+		FreeImage_CloseMemory(hJpegMemory);
+		hJpegMemory = NULL;
+
+		// --- write alpha layer as a sequence of IDAT chunk ---
+		if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) {
+			dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA);
+
+			hPngMemory = FreeImage_OpenMemory();
+			if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) {
+				throw (const char*)NULL;
+			}
+			FreeImage_Unload(dib_alpha);
+			dib_alpha = NULL;
+			// get the IDAT chunk
+			{		
+				BOOL bResult = FALSE;
+				DWORD start_pos = 0;
+				DWORD next_pos = 0;
+				long offset = 8;
+				
+				do {
+					// find the next IDAT chunk from 'offset' position
+					bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos);
+					if(!bResult) break;
+					
+					BYTE *png_data = NULL;
+					DWORD size_in_bytes = 0;
+					
+					// get a pointer to the stream buffer
+					FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes);
+					// write the IDAT chunk
+					mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory);
+
+					offset = next_pos;
+
+				} while(bResult);
+			}
+
+			FreeImage_CloseMemory(hPngMemory);
+			hPngMemory = NULL;
+		}
+
+		// --- write a IEND chunk ---
+		mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory);
+
+		// write the JNG on output stream
+		{
+			BYTE *jng_data = NULL;
+			DWORD size_in_bytes = 0;
+			FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes);
+			io->write_proc(jng_data, 1, size_in_bytes, handle);			
+		}
+
+		FreeImage_CloseMemory(hJngMemory);
+		FreeImage_CloseMemory(hJpegMemory);
+		FreeImage_CloseMemory(hPngMemory);
+
+		return TRUE;
+
+	} catch(const char *text) {
+		FreeImage_CloseMemory(hJngMemory);
+		FreeImage_CloseMemory(hJpegMemory);
+		FreeImage_CloseMemory(hPngMemory);
+		if(dib_rgb && (dib_rgb != dib)) {
+			FreeImage_Unload(dib_rgb);
+		}
+		FreeImage_Unload(dib_alpha);
+		if(text) {
+			FreeImage_OutputMessageProc(format_id, text);
+		}
+		return FALSE;
+	}
+}
diff --git a/files/Source/FreeImage/MemoryIO.cpp b/files/Source/FreeImage/MemoryIO.cpp
new file mode 100644
index 0000000..e099785
--- /dev/null
+++ b/files/Source/FreeImage/MemoryIO.cpp
@@ -0,0 +1,237 @@
+// ==========================================================
+// Memory Input/Output functions
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org> 
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
+// =====================================================================
+
+
+// =====================================================================
+// Open and close a memory handle
+// =====================================================================
+
+FIMEMORY * DLL_CALLCONV 
+FreeImage_OpenMemory(BYTE *data, DWORD size_in_bytes) {
+	// allocate a memory handle
+	FIMEMORY *stream = (FIMEMORY*)malloc(sizeof(FIMEMORY));
+	if(stream) {
+		stream->data = (BYTE*)malloc(sizeof(FIMEMORYHEADER));
+
+		if(stream->data) {
+			FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+			// initialize the memory header
+			memset(mem_header, 0, sizeof(FIMEMORYHEADER));
+			
+			if(data && size_in_bytes) {
+				// wrap a user buffer
+				mem_header->delete_me = FALSE;
+				mem_header->data = (BYTE*)data;
+				mem_header->data_length = mem_header->file_length = size_in_bytes;
+			} else {
+				mem_header->delete_me = TRUE;
+			}
+
+			return stream;
+		}
+		free(stream);
+	}
+
+	return NULL;
+}
+
+
+void DLL_CALLCONV
+FreeImage_CloseMemory(FIMEMORY *stream) {
+	if(stream && stream->data) {
+		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+		if(mem_header->delete_me) {
+			free(mem_header->data);
+		}
+		free(mem_header);
+		free(stream);
+	}
+}
+
+// =====================================================================
+// Memory stream load/save functions
+// =====================================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
+	if (stream && stream->data) {
+		FreeImageIO io;
+		SetMemoryIO(&io);
+
+		return FreeImage_LoadFromHandle(fif, &io, (fi_handle)stream, flags);
+	}
+
+	return NULL;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_SaveToMemory(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags) {
+	if (stream) {
+		FreeImageIO io;
+		SetMemoryIO(&io);
+
+		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+		if(mem_header->delete_me == TRUE) {
+			return FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)stream, flags);
+		} else {
+			// do not save in a user buffer
+			FreeImage_OutputMessageProc(fif, "Memory buffer is read only");
+		}
+	}
+
+	return FALSE;
+}
+
+// =====================================================================
+// Memory stream buffer access
+// =====================================================================
+
+BOOL DLL_CALLCONV
+FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes) {
+	if (stream) {
+		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+		*data = (BYTE*)mem_header->data;
+		*size_in_bytes = mem_header->file_length;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+// =====================================================================
+// Memory stream file type access
+// =====================================================================
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileTypeFromMemory(FIMEMORY *stream, int size) {
+	FreeImageIO io;
+	SetMemoryIO(&io);
+
+	if (stream != NULL) {
+		return FreeImage_GetFileTypeFromHandle(&io, (fi_handle)stream, size);
+	}
+
+	return FIF_UNKNOWN;
+}
+
+// =====================================================================
+// Seeking in Memory stream
+// =====================================================================
+
+/**
+Moves the memory pointer to a specified location
+@param stream Pointer to FIMEMORY structure
+@param offset Number of bytes from origin
+@param origin Initial position
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL DLL_CALLCONV
+FreeImage_SeekMemory(FIMEMORY *stream, long offset, int origin) {
+	FreeImageIO io;
+	SetMemoryIO(&io);
+
+	if (stream != NULL) {
+		int success = io.seek_proc((fi_handle)stream, offset, origin);
+		return (success == 0) ? TRUE : FALSE;
+	}
+
+	return FALSE;
+}
+
+/**
+Gets the current position of a memory pointer
+@param stream Target FIMEMORY structure
+@return Returns the current file position if successful, -1 otherwise
+*/
+long DLL_CALLCONV
+FreeImage_TellMemory(FIMEMORY *stream) {
+	FreeImageIO io;
+	SetMemoryIO(&io);
+
+	if (stream != NULL) {
+		return io.tell_proc((fi_handle)stream);
+	}
+
+	return -1L;
+}
+
+// =====================================================================
+// Reading or Writing in Memory stream
+// =====================================================================
+
+/**
+Reads data from a memory stream
+@param buffer Storage location for data
+@param size Item size in bytes
+@param count Maximum number of items to be read
+@param stream Pointer to FIMEMORY structure
+@return Returns the number of full items actually read, which may be less than count if an error occurs
+*/
+unsigned DLL_CALLCONV 
+FreeImage_ReadMemory(void *buffer, unsigned size, unsigned count, FIMEMORY *stream) {
+	FreeImageIO io;
+	SetMemoryIO(&io);
+
+	if (stream != NULL) {
+		return io.read_proc(buffer, size, count, stream);
+	}
+
+	return 0;
+}
+
+/**
+Writes data to a memory stream.
+@param buffer Pointer to data to be written
+@param size Item size in bytes
+@param count Maximum number of items to be written
+@param stream Pointer to FIMEMORY structure
+@return Returns the number of full items actually written, which may be less than count if an error occurs
+*/
+unsigned DLL_CALLCONV 
+FreeImage_WriteMemory(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream) {
+	if (stream != NULL) {
+		FreeImageIO io;
+		SetMemoryIO(&io);
+
+		FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)stream)->data);
+
+		if(mem_header->delete_me == TRUE) {
+			return io.write_proc((void *)buffer, size, count, stream);
+		} else {
+			// do not write in a user buffer
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Memory buffer is read only");
+		}
+	}
+
+	return 0;
+}
+
diff --git a/files/Source/FreeImage/MultiPage.cpp b/files/Source/FreeImage/MultiPage.cpp
new file mode 100644
index 0000000..c557e02
--- /dev/null
+++ b/files/Source/FreeImage/MultiPage.cpp
@@ -0,0 +1,977 @@
+// ==========================================================
+// Multi-Page functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Laurent Rocher (rocherl@club-internet.fr)
+// - Steve Johnson (steve@parisgroup.net)
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net
+// - Martin Dyring-Andersen (mda@spamfighter.com)
+// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "CacheFile.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+#include "Utilities.h"
+#include "FreeImage.h"
+
+// ----------------------------------------------------------
+
+enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE };
+
+// ----------------------------------------------------------
+
+struct BlockTypeS {
+	BlockType m_type;
+
+	BlockTypeS(BlockType type) : m_type(type) {
+	}
+	virtual ~BlockTypeS() {}
+};
+
+struct BlockContinueus : public BlockTypeS {
+	int       m_start;
+	int       m_end;
+
+	BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS),
+	m_start(s),
+	m_end(e) {
+	}	
+};
+
+struct BlockReference : public BlockTypeS {
+	int       m_reference;
+	int       m_size;
+
+	BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE),
+	m_reference(r),
+	m_size(size) {
+	}
+};
+
+// ----------------------------------------------------------
+
+typedef std::list<BlockTypeS *> BlockList;
+typedef std::list<BlockTypeS *>::iterator BlockListIterator;
+
+// ----------------------------------------------------------
+
+FI_STRUCT (MULTIBITMAPHEADER) {
+	PluginNode *node;
+	FREE_IMAGE_FORMAT fif;
+	FreeImageIO *io;
+	fi_handle handle;
+	CacheFile *m_cachefile;
+	std::map<FIBITMAP *, int> locked_pages;
+	BOOL changed;
+	int page_count;
+	BlockList m_blocks;
+	char *m_filename;
+	BOOL read_only;
+	FREE_IMAGE_FORMAT cache_fif;
+	int load_flags;
+};
+
+// =====================================================================
+// Helper functions
+// =====================================================================
+
+inline void
+ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
+	size_t lastDot = src_filename.find_last_of('.');
+	if (lastDot == std::string::npos) {
+		dst_filename = src_filename;
+		dst_filename += ".";
+		dst_filename += dst_extension;
+	}
+	else {
+		dst_filename = src_filename.substr(0, lastDot + 1);
+		dst_filename += dst_extension;
+	}
+}
+
+// =====================================================================
+// Internal Multipage functions
+// =====================================================================
+
+inline MULTIBITMAPHEADER *
+FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
+	return (MULTIBITMAPHEADER *)bitmap->data;
+}
+
+static BlockListIterator DLL_CALLCONV
+FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
+	assert(NULL != bitmap);
+
+	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+	// step 1: find the block that matches the given position
+
+	int prev_count = 0;
+	int count = 0;
+	BlockListIterator i;
+	BlockTypeS *current_block = NULL;
+
+	for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+		prev_count = count;
+
+		switch((*i)->m_type) {
+			case BLOCK_CONTINUEUS :
+				count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
+				break;
+
+			case BLOCK_REFERENCE :
+				count++;
+				break;
+		}
+
+		current_block = *i;
+
+		if (count > position)
+			break;
+	}
+
+	// step 2: make sure we found the node. from here it gets a little complicated:
+	// * if the block is there, just return it
+	// * if the block is a series of blocks, split it in max 3 new blocks
+	//   and return the splitted block
+
+	if ((current_block) && (count > position)) {
+		switch(current_block->m_type) {
+			case BLOCK_REFERENCE :
+				return i;
+
+			case BLOCK_CONTINUEUS :
+			{
+				BlockContinueus *block = (BlockContinueus *)current_block;
+
+				if (block->m_start != block->m_end) {
+					int item = block->m_start + (position - prev_count);
+
+					// left part
+
+					if (item != block->m_start) {
+						BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
+						header->m_blocks.insert(i, (BlockTypeS *)block_a);
+					}
+
+					// middle part
+
+					BlockContinueus *block_b = new BlockContinueus(item, item);
+					BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
+
+					// right part
+
+					if (item != block->m_end) {
+						BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
+						header->m_blocks.insert(i, (BlockTypeS *)block_c);
+					}
+
+					// remove the old block that was just splitted
+
+					header->m_blocks.remove((BlockTypeS *)block);
+					delete block;
+
+					// return the splitted block
+					
+					return block_target;
+				}
+
+				return i;
+			}
+		}
+	}
+	// we should never go here ...
+	assert(false);
+	return header->m_blocks.end();
+}
+
+int DLL_CALLCONV
+FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {	
+	if (bitmap) {
+		if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
+			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+			header->io->seek_proc(header->handle, 0, SEEK_SET);
+
+   			void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+
+			int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
+
+			FreeImage_Close(header->node, header->io, header->handle, data);
+
+			return page_count;
+		}
+	}
+
+	return 0;
+}
+
+// =====================================================================
+// Multipage functions
+// =====================================================================
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
+
+	FILE *handle = NULL;
+	try {
+		// sanity check on the parameters
+
+		if (create_new) {
+			read_only = FALSE;
+		}
+
+		// retrieve the plugin list to find the node belonging to this plugin
+
+		PluginList *list = FreeImage_GetPluginList();
+
+		if (list) {
+			PluginNode *node = list->FindNodeFromFIF(fif);
+
+			if (node) {
+				std::unique_ptr<FreeImageIO> io (new FreeImageIO);
+
+				SetDefaultIO(io.get());
+
+				if (!create_new) {
+					handle = fopen(filename, "rb");
+					if (handle == NULL) {
+						return NULL;
+					}
+				}
+
+				std::unique_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
+				std::unique_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
+				header->m_filename = new char[strlen(filename) + 1];
+				strcpy(header->m_filename, filename);
+				header->node = node;
+				header->fif = fif;
+				header->io = io.get ();
+				header->handle = handle;						
+				header->changed = FALSE;						
+				header->read_only = read_only;
+				header->m_cachefile = NULL;
+				header->cache_fif = fif;
+				header->load_flags = flags;
+
+				// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+				bitmap->data = header.get();
+
+				// cache the page count
+
+				header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
+
+				// allocate a continueus block to describe the bitmap
+
+				if (!create_new) {
+					header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+				}
+
+				// set up the cache
+
+				if (!read_only) {
+					std::string cache_name;
+					ReplaceExtension(cache_name, filename, "ficache");
+
+                                        std::unique_ptr<CacheFile> cache_file(
+                                            new CacheFile(
+                                                cache_name,
+                                                keep_cache_in_memory));
+
+                                        if (cache_file->open()) {
+						// we can use release() as std::bad_alloc won't be thrown from here on
+						header->m_cachefile = cache_file.release();
+					} else {
+						// an error occured ...
+						fclose(handle);
+						return NULL;
+					}
+				}
+				// return the multibitmap
+				// std::bad_alloc won't be thrown from here on
+				header.release(); // now owned by bitmap
+				io.release();	  // now owned by bitmap
+				return bitmap.release(); // now owned by caller
+			}
+		}
+	} catch (std::bad_alloc &) {
+		/** @todo report error */
+	}
+	if (handle)
+		fclose(handle);
+	return NULL;
+}
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
+	try {
+		BOOL read_only = FALSE;	// modifications (if any) will be stored into the memory cache
+
+		if (io && handle) {
+		
+			// retrieve the plugin list to find the node belonging to this plugin
+			PluginList *list = FreeImage_GetPluginList();
+		
+			if (list) {
+				PluginNode *node = list->FindNodeFromFIF(fif);
+			
+				if (node) {
+					std::unique_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
+					std::unique_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
+					std::unique_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
+					header->io = tmp_io.get();
+					header->m_filename = NULL;
+					header->node = node;
+					header->fif = fif;
+					header->handle = handle;						
+					header->changed = FALSE;						
+					header->read_only = read_only;	
+					header->m_cachefile = NULL;
+					header->cache_fif = fif;
+					header->load_flags = flags;
+							
+					// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+					bitmap->data = header.get();
+
+					// cache the page count
+
+					header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
+
+					// allocate a continueus block to describe the bitmap
+
+					header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+
+					if (!read_only) {
+						// set up the cache
+						std::unique_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
+						
+						if (cache_file->open()) {
+							header->m_cachefile = cache_file.release();
+						}
+					}
+					tmp_io.release();
+					header.release();
+					return bitmap.release();
+				}
+			}
+		}
+	} catch (std::bad_alloc &) {
+		/** @todo report error */
+	}
+	return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
+	if(!bitmap || !bitmap->data || !io || !handle) {
+		return FALSE;
+	}
+
+	BOOL success = TRUE;
+
+	// retrieve the plugin list to find the node belonging to this plugin
+	PluginList *list = FreeImage_GetPluginList();
+	
+	if (list) {
+		PluginNode *node = list->FindNodeFromFIF(fif);
+
+		if(node) {
+			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+			
+			// dst data
+			void *data = FreeImage_Open(node, io, handle, FALSE);
+			// src data
+			void *data_read = NULL;
+			
+			if(header->handle) {
+				// open src
+				header->io->seek_proc(header->handle, 0, SEEK_SET);
+				data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+			}
+			
+			// write all the pages to the file using handle and io
+			
+			int count = 0;
+			
+			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
+				if (success) {
+					switch((*i)->m_type) {
+						case BLOCK_CONTINUEUS:
+						{
+							BlockContinueus *block = (BlockContinueus *)(*i);
+							
+							for (int j = block->m_start; j <= block->m_end; j++) {
+
+								// load the original source data
+								FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
+								
+								// save the data
+								success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
+								count++;
+								
+								FreeImage_Unload(dib);
+							}
+							
+							break;
+						}
+						
+						case BLOCK_REFERENCE:
+						{
+							BlockReference *ref = (BlockReference *)(*i);
+							
+							// read the compressed data
+							
+							BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
+							
+							header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
+							
+							// uncompress the data
+							
+							FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
+							FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
+							FreeImage_CloseMemory(hmem);
+							
+							// get rid of the buffer
+							free(compressed_data);
+							
+							// save the data
+							
+							success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
+							count++;
+							
+							// unload the dib
+
+							FreeImage_Unload(dib);
+
+							break;
+						}
+					}
+				} else {
+					break;
+				}
+			}
+			
+			// close the files
+			
+			FreeImage_Close(header->node, header->io, header->handle, data_read);
+
+			FreeImage_Close(node, io, handle, data); 
+			
+			return success;
+		}
+	}
+
+	return FALSE;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
+	if (bitmap) {
+		BOOL success = TRUE;
+		
+		if (bitmap->data) {
+			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);			
+			
+			// saves changes only of images loaded directly from a file
+			if (header->changed && header->m_filename) {
+				try {
+					// open a temp file
+
+					std::string spool_name;
+
+					ReplaceExtension(spool_name, header->m_filename, "fispool");
+
+					// open the spool file and the source file
+        
+					FILE *f = fopen(spool_name.c_str(), "w+b");
+				
+					// saves changes
+					if (f == NULL) {
+						FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
+						success = FALSE;
+					} else {
+						success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
+
+						// close the files
+
+						if (fclose(f) != 0) {
+							success = FALSE;
+							FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
+						}
+					}
+					if (header->handle) {
+						fclose((FILE *)header->handle);
+					}
+				
+					// applies changes to the destination file
+
+					if (success) {
+						remove(header->m_filename);
+						success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
+						if(!success) {
+							FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
+						}
+					} else {
+						remove(spool_name.c_str());
+					}
+				} catch (std::bad_alloc &) {
+					success = FALSE;
+				}
+
+			} else {
+				if (header->handle && header->m_filename) {
+					fclose((FILE *)header->handle);
+				}
+			}
+
+			// clear the blocks list
+
+			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+				delete *i;
+			}
+
+			// flush and dispose the cache
+
+			if (header->m_cachefile) {
+				header->m_cachefile->close();
+				delete header->m_cachefile;
+			}
+
+			// delete the last open bitmaps
+
+			while (!header->locked_pages.empty()) {
+				FreeImage_Unload(header->locked_pages.begin()->first);
+
+				header->locked_pages.erase(header->locked_pages.begin()->first);
+			}
+
+			// get rid of the IO structure
+
+			delete header->io;
+
+			// delete the filename
+
+			if(header->m_filename) {
+				delete[] header->m_filename;
+			}
+
+			// delete the FIMULTIBITMAPHEADER
+
+			delete header;
+		}
+
+		delete bitmap;
+
+		return success;
+	}
+
+	return FALSE;
+}
+
+int DLL_CALLCONV
+FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
+	if (bitmap) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		if (header->page_count == -1) {
+			header->page_count = 0;
+
+			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+				switch((*i)->m_type) {
+					case BLOCK_CONTINUEUS :
+						header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
+						break;
+
+					case BLOCK_REFERENCE :
+						header->page_count++;
+						break;
+				}
+			}
+		}
+
+		return header->page_count;
+	}
+
+	return 0;
+}
+
+static BlockReference* 
+FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
+	if (header->read_only || !header->locked_pages.empty())
+		return NULL;
+
+	DWORD compressed_size = 0;
+	BYTE *compressed_data = NULL;
+
+	// compress the bitmap data
+
+	// open a memory handle
+	FIMEMORY *hmem = FreeImage_OpenMemory();
+	if(hmem==NULL) return NULL;
+	// save the file to memory
+	if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
+		FreeImage_CloseMemory(hmem);
+		return NULL;
+	}
+	// get the buffer from the memory stream
+	if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
+		FreeImage_CloseMemory(hmem);
+		return NULL;
+	}
+
+	// write the compressed data to the cache
+	int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
+	// get rid of the compressed data
+	FreeImage_CloseMemory(hmem);
+
+	return new(std::nothrow) BlockReference(ref, compressed_size);
+}
+
+void DLL_CALLCONV
+FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
+	if (!bitmap || !data) 
+		return;
+
+	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+	BlockReference *block = FreeImage_SavePageToBlock(header, data);
+	if(block==NULL) return;
+
+	// add the block
+	header->m_blocks.push_back((BlockTypeS *)block);
+	header->changed = TRUE;
+	header->page_count = -1;
+}
+
+void DLL_CALLCONV
+FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
+	if (!bitmap || !data) 
+		return;
+
+	if (page >= FreeImage_GetPageCount(bitmap)) 
+		return;
+			
+	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+	BlockReference *block = FreeImage_SavePageToBlock(header, data);
+	if(block==NULL) return;
+
+	// add a block
+	if (page > 0) {
+		BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);		
+
+		header->m_blocks.insert(block_source, (BlockTypeS *)block);
+	} else {
+		header->m_blocks.push_front((BlockTypeS *)block);
+	}
+
+	header->changed = TRUE;
+	header->page_count = -1;
+}
+
+void DLL_CALLCONV
+FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
+	if (bitmap) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		if ((!header->read_only) && (header->locked_pages.empty())) {
+			if (FreeImage_GetPageCount(bitmap) > 1) {
+				BlockListIterator i = FreeImage_FindBlock(bitmap, page);
+
+				if (i != header->m_blocks.end()) {
+					switch((*i)->m_type) {
+						case BLOCK_CONTINUEUS :
+							delete *i;
+							header->m_blocks.erase(i);
+							break;
+
+						case BLOCK_REFERENCE :
+							header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
+							delete *i;
+							header->m_blocks.erase(i);
+							break;
+					}
+
+					header->changed = TRUE;
+					header->page_count = -1;
+				}
+			}
+		}
+	}
+}
+
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
+	if (bitmap) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		// only lock if the page wasn't locked before...
+
+		for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
+			if (i->second == page) {
+				return NULL;
+			}
+		}
+
+		// open the bitmap
+
+		header->io->seek_proc(header->handle, 0, SEEK_SET);
+
+   		void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+
+		// load the bitmap data
+
+		if (data != NULL) {
+			FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL;
+
+			// close the file
+
+			FreeImage_Close(header->node, header->io, header->handle, data);
+
+			// if there was still another bitmap open, get rid of it
+
+			if (dib) {
+				header->locked_pages[dib] = page;
+
+				return dib;
+			}	
+
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
+	if ((bitmap) && (page)) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		// find out if the page we try to unlock is actually locked...
+
+		if (header->locked_pages.find(page) != header->locked_pages.end()) {
+			// store the bitmap compressed in the cache for later writing
+
+			if (changed && !header->read_only) {
+				header->changed = TRUE;
+
+				// cut loose the block from the rest
+
+				BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
+
+				// compress the data
+
+				DWORD compressed_size = 0;
+				BYTE *compressed_data = NULL;
+
+				// open a memory handle
+				FIMEMORY *hmem = FreeImage_OpenMemory();
+				// save the page to memory
+				FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
+				// get the buffer from the memory stream
+				FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
+
+				// write the data to the cache
+
+				switch ((*i)->m_type) {
+					case BLOCK_CONTINUEUS :
+					{
+						int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
+
+						delete (*i);
+
+						*i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
+
+						break;
+					}
+
+					case BLOCK_REFERENCE :
+					{
+						BlockReference *reference = (BlockReference *)(*i);
+
+						header->m_cachefile->deleteFile(reference->m_reference);
+
+						delete (*i);
+
+						int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
+
+						*i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
+
+						break;
+					}
+				}
+
+				// get rid of the compressed data
+
+				FreeImage_CloseMemory(hmem);
+			}
+
+			// reset the locked page so that another page can be locked
+
+			FreeImage_Unload(page);
+
+			header->locked_pages.erase(page);
+		}
+	}
+}
+
+BOOL DLL_CALLCONV
+FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
+	if (bitmap) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		if ((!header->read_only) && (header->locked_pages.empty())) {
+			if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
+				BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
+				BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
+
+				header->m_blocks.insert(block_target, *block_source);			
+				header->m_blocks.erase(block_source);
+
+				header->changed = TRUE;
+
+				return TRUE;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
+	if ((bitmap) && (count)) {
+		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+		if ((pages == NULL) || (*count == 0)) {
+			*count = (int)header->locked_pages.size();
+		} else {
+			int c = 0;
+
+			for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
+				pages[c] = i->second;
+
+				c++;
+
+				if (c == *count)
+					break;
+			}
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+// =====================================================================
+// Memory IO Multipage functions
+// =====================================================================
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
+	BOOL read_only = FALSE;	// modifications (if any) will be stored into the memory cache
+
+	// retrieve the plugin list to find the node belonging to this plugin
+
+	PluginList *list = FreeImage_GetPluginList();
+
+	if (list) {
+		PluginNode *node = list->FindNodeFromFIF(fif);
+
+		if (node) {
+			FreeImageIO *io = new(std::nothrow) FreeImageIO;
+
+			if (io) {
+				SetMemoryIO(io);
+
+				FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
+
+				if (bitmap) {
+					MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
+
+					if (header) {
+						header->m_filename = NULL;
+						header->node = node;
+						header->fif = fif;
+						header->io = io;
+						header->handle = (fi_handle)stream;						
+						header->changed = FALSE;						
+						header->read_only = read_only;
+						header->m_cachefile = NULL;
+						header->cache_fif = fif;
+						header->load_flags = flags;
+
+						// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+						bitmap->data = header;
+
+						// cache the page count
+
+						header->page_count = FreeImage_InternalGetPageCount(bitmap);
+
+						// allocate a continueus block to describe the bitmap
+
+						header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+						
+						if (!read_only) {
+							// set up the cache
+							CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
+							
+							if (cache_file && cache_file->open()) {
+								header->m_cachefile = cache_file;
+							}
+						}
+
+						return bitmap;
+					}
+					
+					delete bitmap;
+				}
+				
+				delete io;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
+	if (stream && stream->data) {
+		FreeImageIO io;
+		SetMemoryIO(&io);
+
+		return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
+	}
+
+	return FALSE;
+}
diff --git a/files/Source/FreeImage/NNQuantizer.cpp b/files/Source/FreeImage/NNQuantizer.cpp
new file mode 100644
index 0000000..f907c41
--- /dev/null
+++ b/files/Source/FreeImage/NNQuantizer.cpp
@@ -0,0 +1,507 @@
+// NeuQuant Neural-Net Quantization Algorithm
+// ------------------------------------------
+//
+// Copyright (c) 1994 Anthony Dekker
+//
+// NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
+// See "Kohonen neural networks for optimal colour quantization"
+// in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
+// for a discussion of the algorithm.
+//
+// Any party obtaining a copy of these files from the author, directly or
+// indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+// world-wide, paid up, royalty-free, nonexclusive right and license to deal
+// in this software and documentation files (the "Software"), including without
+// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons who receive
+// copies from any such party to do so, with the only requirement being
+// that this copyright notice remain intact.
+
+///////////////////////////////////////////////////////////////////////
+// History
+// -------
+// January 2001: Adaptation of the Neural-Net Quantization Algorithm
+//               for the FreeImage 2 library
+//               Author: Hervé Drolon (drolon@infonie.fr)
+// March 2004:   Adaptation for the FreeImage 3 library (port to big endian processors)
+//               Author: Hervé Drolon (drolon@infonie.fr)
+// April 2004:   Algorithm rewritten as a C++ class. 
+//               Fixed a bug in the algorithm with handling of 4-byte boundary alignment.
+//               Author: Hervé Drolon (drolon@infonie.fr)
+///////////////////////////////////////////////////////////////////////
+
+#include "Quantizers.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+
+// Four primes near 500 - assume no image has a length so large
+// that it is divisible by all four primes
+// ==========================================================
+
+#define prime1		499
+#define prime2		491
+#define prime3		487
+#define prime4		503
+
+// ----------------------------------------------------------------
+
+NNQuantizer::NNQuantizer(int PaletteSize)
+{
+	netsize = PaletteSize;
+	maxnetpos = netsize - 1;
+	initrad = netsize < 8 ? 1 : (netsize >> 3);
+	initradius = (initrad * radiusbias);
+
+	network = NULL;
+
+	network = (pixel *)malloc(netsize * sizeof(pixel));
+	bias = (int *)malloc(netsize * sizeof(int));
+	freq = (int *)malloc(netsize * sizeof(int));
+	radpower = (int *)malloc(initrad * sizeof(int));
+
+	if( !network || !bias || !freq || !radpower ) {
+		if(network) free(network);
+		if(bias) free(bias);
+		if(freq) free(freq);
+		if(radpower) free(radpower);
+		throw FI_MSG_ERROR_MEMORY;
+	}
+}
+
+NNQuantizer::~NNQuantizer()
+{
+	if(network) free(network);
+	if(bias) free(bias);
+	if(freq) free(freq);
+	if(radpower) free(radpower);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Initialise network in range (0,0,0) to (255,255,255) and set parameters
+// -----------------------------------------------------------------------
+
+void NNQuantizer::initnet() {
+	int i, *p;
+
+	for (i = 0; i < netsize; i++) {
+		p = network[i];
+		p[FI_RGBA_BLUE] = p[FI_RGBA_GREEN] = p[FI_RGBA_RED] = (i << (netbiasshift+8))/netsize;
+		freq[i] = intbias/netsize;	/* 1/netsize */
+		bias[i] = 0;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////////////	
+// Unbias network to give byte values 0..255 and record position i to prepare for sort
+// ------------------------------------------------------------------------------------
+
+void NNQuantizer::unbiasnet() {
+	int i, j, temp;
+
+	for (i = 0; i < netsize; i++) {
+		for (j = 0; j < 3; j++) {
+			// OLD CODE: network[i][j] >>= netbiasshift; 
+			// Fix based on bug report by Juergen Weigert jw@suse.de
+			temp = (network[i][j] + (1 << (netbiasshift - 1))) >> netbiasshift;
+			if (temp > 255) temp = 255;
+			network[i][j] = temp;
+		}
+		network[i][3] = i;			// record colour no 
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Insertion sort of network and building of netindex[0..255] (to do after unbias)
+// -------------------------------------------------------------------------------
+
+void NNQuantizer::inxbuild() {
+	int i,j,smallpos,smallval;
+	int *p,*q;
+	int previouscol,startpos;
+
+	previouscol = 0;
+	startpos = 0;
+	for (i = 0; i < netsize; i++) {
+		p = network[i];
+		smallpos = i;
+		smallval = p[FI_RGBA_GREEN];			// index on g
+		// find smallest in i..netsize-1
+		for (j = i+1; j < netsize; j++) {
+			q = network[j];
+			if (q[FI_RGBA_GREEN] < smallval) {	// index on g
+				smallpos = j;
+				smallval = q[FI_RGBA_GREEN];	// index on g
+			}
+		}
+		q = network[smallpos];
+		// swap p (i) and q (smallpos) entries
+		if (i != smallpos) {
+			j = q[FI_RGBA_BLUE];  q[FI_RGBA_BLUE]  = p[FI_RGBA_BLUE];  p[FI_RGBA_BLUE]  = j;
+			j = q[FI_RGBA_GREEN]; q[FI_RGBA_GREEN] = p[FI_RGBA_GREEN]; p[FI_RGBA_GREEN] = j;
+			j = q[FI_RGBA_RED];   q[FI_RGBA_RED]   = p[FI_RGBA_RED];   p[FI_RGBA_RED]   = j;
+			j = q[3];   q[3] = p[3];   p[3] = j;
+		}
+		// smallval entry is now in position i
+		if (smallval != previouscol) {
+			netindex[previouscol] = (startpos+i)>>1;
+			for (j = previouscol+1; j < smallval; j++)
+				netindex[j] = i;
+			previouscol = smallval;
+			startpos = i;
+		}
+	}
+	netindex[previouscol] = (startpos+maxnetpos)>>1;
+	for (j = previouscol+1; j < 256; j++)
+		netindex[j] = maxnetpos; // really 256
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Search for BGR values 0..255 (after net is unbiased) and return colour index
+// ----------------------------------------------------------------------------
+
+int NNQuantizer::inxsearch(int b, int g, int r) {
+	int i, j, dist, a, bestd;
+	int *p;
+	int best;
+
+	bestd = 1000;		// biggest possible dist is 256*3
+	best = -1;
+	i = netindex[g];	// index on g
+	j = i-1;			// start at netindex[g] and work outwards
+
+	while ((i < netsize) || (j >= 0)) {
+		if (i < netsize) {
+			p = network[i];
+			dist = p[FI_RGBA_GREEN] - g;				// inx key
+			if (dist >= bestd)
+				i = netsize;	// stop iter
+			else {
+				i++;
+				if (dist < 0)
+					dist = -dist;
+				a = p[FI_RGBA_BLUE] - b;
+				if (a < 0)
+					a = -a;
+				dist += a;
+				if (dist < bestd) {
+					a = p[FI_RGBA_RED] - r;
+					if (a<0)
+						a = -a;
+					dist += a;
+					if (dist < bestd) {
+						bestd = dist;
+						best = p[3];
+					}
+				}
+			}
+		}
+		if (j >= 0) {
+			p = network[j];
+			dist = g - p[FI_RGBA_GREEN];			// inx key - reverse dif
+			if (dist >= bestd)
+				j = -1;	// stop iter
+			else {
+				j--;
+				if (dist < 0)
+					dist = -dist;
+				a = p[FI_RGBA_BLUE] - b;
+				if (a<0)
+					a = -a;
+				dist += a;
+				if (dist < bestd) {
+					a = p[FI_RGBA_RED] - r;
+					if (a<0)
+						a = -a;
+					dist += a;
+					if (dist < bestd) {
+						bestd = dist;
+						best = p[3];
+					}
+				}
+			}
+		}
+	}
+	return best;
+}
+
+///////////////////////////////
+// Search for biased BGR values
+// ----------------------------
+
+int NNQuantizer::contest(int b, int g, int r) {
+	// finds closest neuron (min dist) and updates freq
+	// finds best neuron (min dist-bias) and returns position
+	// for frequently chosen neurons, freq[i] is high and bias[i] is negative
+	// bias[i] = gamma*((1/netsize)-freq[i])
+
+	int i,dist,a,biasdist,betafreq;
+	int bestpos,bestbiaspos,bestd,bestbiasd;
+	int *p,*f, *n;
+
+	bestd = ~(((int) 1)<<31);
+	bestbiasd = bestd;
+	bestpos = -1;
+	bestbiaspos = bestpos;
+	p = bias;
+	f = freq;
+
+	for (i = 0; i < netsize; i++) {
+		n = network[i];
+		dist = n[FI_RGBA_BLUE] - b;
+		if (dist < 0)
+			dist = -dist;
+		a = n[FI_RGBA_GREEN] - g;
+		if (a < 0)
+			a = -a;
+		dist += a;
+		a = n[FI_RGBA_RED] - r;
+		if (a < 0)
+			a = -a;
+		dist += a;
+		if (dist < bestd) {
+			bestd = dist;
+			bestpos = i;
+		}
+		biasdist = dist - ((*p)>>(intbiasshift-netbiasshift));
+		if (biasdist < bestbiasd) {
+			bestbiasd = biasdist;
+			bestbiaspos = i;
+		}
+		betafreq = (*f >> betashift);
+		*f++ -= betafreq;
+		*p++ += (betafreq << gammashift);
+	}
+	freq[bestpos] += beta;
+	bias[bestpos] -= betagamma;
+	return bestbiaspos;
+}
+
+///////////////////////////////////////////////////////
+// Move neuron i towards biased (b,g,r) by factor alpha
+// ---------------------------------------------------- 
+
+void NNQuantizer::altersingle(int alpha, int i, int b, int g, int r) {
+	int *n;
+
+	n = network[i];				// alter hit neuron
+	n[FI_RGBA_BLUE]	 -= (alpha * (n[FI_RGBA_BLUE]  - b)) / initalpha;
+	n[FI_RGBA_GREEN] -= (alpha * (n[FI_RGBA_GREEN] - g)) / initalpha;
+	n[FI_RGBA_RED]   -= (alpha * (n[FI_RGBA_RED]   - r)) / initalpha;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
+// ---------------------------------------------------------------------------------
+
+void NNQuantizer::alterneigh(int rad, int i, int b, int g, int r) {
+	int j, k, lo, hi, a;
+	int *p, *q;
+
+	lo = i - rad;   if (lo < -1) lo = -1;
+	hi = i + rad;   if (hi > netsize) hi = netsize;
+
+	j = i+1;
+	k = i-1;
+	q = radpower;
+	while ((j < hi) || (k > lo)) {
+		a = (*(++q));
+		if (j < hi) {
+			p = network[j];
+			p[FI_RGBA_BLUE]  -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
+			p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
+			p[FI_RGBA_RED]   -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
+			j++;
+		}
+		if (k > lo) {
+			p = network[k];
+			p[FI_RGBA_BLUE]  -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
+			p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
+			p[FI_RGBA_RED]   -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
+			k--;
+		}
+	}
+}
+
+/////////////////////
+// Main Learning Loop
+// ------------------
+
+/**
+ Get a pixel sample at position pos. Handle 4-byte boundary alignment.
+ @param pos pixel position in a WxHx3 pixel buffer
+ @param b blue pixel component
+ @param g green pixel component
+ @param r red pixel component
+*/
+void NNQuantizer::getSample(long pos, int *b, int *g, int *r) {
+	// get equivalent pixel coordinates 
+	// - assume it's a 24-bit image -
+	int x = pos % img_line;
+	int y = pos / img_line;
+
+	BYTE *bits = FreeImage_GetScanLine(dib_ptr, y) + x;
+
+	*b = bits[FI_RGBA_BLUE] << netbiasshift;
+	*g = bits[FI_RGBA_GREEN] << netbiasshift;
+	*r = bits[FI_RGBA_RED] << netbiasshift;
+}
+
+void NNQuantizer::learn(int sampling_factor) {
+	int i, j, b, g, r;
+	int radius, rad, alpha, step, delta, samplepixels;
+	int alphadec; // biased by 10 bits
+	long pos, lengthcount;
+
+	// image size as viewed by the scan algorithm
+	lengthcount = img_width * img_height * 3;
+
+	// number of samples used for the learning phase
+	samplepixels = lengthcount / (3 * sampling_factor);
+
+	// decrease learning rate after delta pixel presentations
+	delta = samplepixels / ncycles;
+	if(delta == 0) {
+		// avoid a 'divide by zero' error with very small images
+		delta = 1;
+	}
+
+	// initialize learning parameters
+	alphadec = 30 + ((sampling_factor - 1) / 3);
+	alpha = initalpha;
+	radius = initradius;
+	
+	rad = radius >> radiusbiasshift;
+	if (rad <= 1) rad = 0;
+	for (i = 0; i < rad; i++) 
+		radpower[i] = alpha*(((rad*rad - i*i)*radbias)/(rad*rad));
+	
+	// initialize pseudo-random scan
+	if ((lengthcount % prime1) != 0)
+		step = 3*prime1;
+	else {
+		if ((lengthcount % prime2) != 0)
+			step = 3*prime2;
+		else {
+			if ((lengthcount % prime3) != 0) 
+				step = 3*prime3;
+			else
+				step = 3*prime4;
+		}
+	}
+	
+	i = 0;		// iteration
+	pos = 0;	// pixel position
+
+	while (i < samplepixels) {
+		// get next learning sample
+		getSample(pos, &b, &g, &r);
+
+		// find winning neuron
+		j = contest(b, g, r);
+
+		// alter winner
+		altersingle(alpha, j, b, g, r);
+
+		// alter neighbours 
+		if (rad) alterneigh(rad, j, b, g, r);
+
+		// next sample
+		pos += step;
+		while (pos >= lengthcount) pos -= lengthcount;
+	
+		i++;
+		if (i % delta == 0) {	
+			// decrease learning rate and also the neighborhood
+			alpha -= alpha / alphadec;
+			radius -= radius / radiusdec;
+			rad = radius >> radiusbiasshift;
+			if (rad <= 1) rad = 0;
+			for (j = 0; j < rad; j++) 
+				radpower[j] = alpha * (((rad*rad - j*j) * radbias) / (rad*rad));
+		}
+	}
+	
+}
+
+//////////////
+// Quantizer
+// -----------
+
+FIBITMAP* NNQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette, int sampling) {
+
+	if ((!dib) || (FreeImage_GetBPP(dib) != 24)) {
+		return NULL;
+	}
+
+	// 1) Select a sampling factor in range 1..30 (input parameter 'sampling')
+	//    1 => slower, 30 => faster. Default value is 1
+
+
+	// 2) Get DIB parameters
+
+	dib_ptr = dib;
+	
+	img_width  = FreeImage_GetWidth(dib);	// DIB width
+	img_height = FreeImage_GetHeight(dib);	// DIB height
+	img_line   = FreeImage_GetLine(dib);	// DIB line length in bytes (should be equal to 3 x W)
+
+	// For small images, adjust the sampling factor to avoid a 'divide by zero' error later 
+	// (see delta in learn() routine)
+	int adjust = (img_width * img_height) / ncycles;
+	if(sampling >= adjust)
+		sampling = 1;
+
+
+	// 3) Initialize the network and apply the learning algorithm
+
+	if( netsize > ReserveSize ) {
+		netsize -= ReserveSize;
+		initnet();
+		learn(sampling);
+		unbiasnet();
+		netsize += ReserveSize;
+	}
+
+	// 3.5) Overwrite the last few palette entries with the reserved ones
+    for (int i = 0; i < ReserveSize; i++) {
+		network[netsize - ReserveSize + i][FI_RGBA_BLUE] = ReservePalette[i].rgbBlue;
+		network[netsize - ReserveSize + i][FI_RGBA_GREEN] = ReservePalette[i].rgbGreen;
+		network[netsize - ReserveSize + i][FI_RGBA_RED] = ReservePalette[i].rgbRed;
+		network[netsize - ReserveSize + i][3] = netsize - ReserveSize + i;
+	}
+
+	// 4) Allocate a new 8-bit DIB
+
+	FIBITMAP *new_dib = FreeImage_Allocate(img_width, img_height, 8);
+
+	if (new_dib == NULL)
+		return NULL;
+
+	// 5) Write the quantized palette
+
+	RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+    for (int j = 0; j < netsize; j++) {
+		new_pal[j].rgbBlue  = (BYTE)network[j][FI_RGBA_BLUE];
+		new_pal[j].rgbGreen = (BYTE)network[j][FI_RGBA_GREEN];
+		new_pal[j].rgbRed	= (BYTE)network[j][FI_RGBA_RED];
+	}
+
+	inxbuild();
+
+	// 6) Write output image using inxsearch(b,g,r)
+
+	for (WORD rows = 0; rows < img_height; rows++) {
+		BYTE *new_bits = FreeImage_GetScanLine(new_dib, rows);			
+		BYTE *bits = FreeImage_GetScanLine(dib_ptr, rows);
+
+		for (WORD cols = 0; cols < img_width; cols++) {
+			new_bits[cols] = (BYTE)inxsearch(bits[FI_RGBA_BLUE], bits[FI_RGBA_GREEN], bits[FI_RGBA_RED]);
+
+			bits += 3;
+		}
+	}
+
+	return (FIBITMAP*) new_dib;
+}
diff --git a/files/Source/FreeImage/PSDParser.cpp b/files/Source/FreeImage/PSDParser.cpp
new file mode 100644
index 0000000..fba54c2
--- /dev/null
+++ b/files/Source/FreeImage/PSDParser.cpp
@@ -0,0 +1,1057 @@
+// ==========================================================
+// Photoshop Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "PSDParser.h"
+
+// --------------------------------------------------------------------------
+
+// PSD signature (= '8BPS')
+#define PSD_SIGNATURE	0x38425053
+// Image resource block signature (= '8BIM')
+#define PSD_RESOURCE	0x3842494D 
+
+// PSD color modes
+#define PSDP_BITMAP			0
+#define PSDP_GRAYSCALE		1
+#define PSDP_INDEXED		2
+#define PSDP_RGB			3
+#define PSDP_CMYK			4
+#define PSDP_MULTICHANNEL	7
+#define PSDP_DUOTONE		8
+#define PSDP_LAB			9
+
+// PSD compression schemes
+#define PSDP_COMPRESSION_NONE	0	// Raw data
+#define PSDP_COMPRESSION_RLE	1	// RLE compression (same as TIFF packed bits)
+
+#define SAFE_DELETE_ARRAY(_p_) { if (NULL != (_p_)) { delete [] (_p_); (_p_) = NULL; } }
+
+// --------------------------------------------------------------------------
+
+static inline int 
+psdGetValue(const BYTE * iprBuffer, const int iBytes) {
+	int v = iprBuffer[0];
+	for (int i=1; i<iBytes; ++i) {
+		v = (v << 8) | iprBuffer[i];
+	}
+	return v;
+}
+
+// --------------------------------------------------------------------------
+
+psdHeaderInfo::psdHeaderInfo() : _Channels(-1), _Height(-1), _Width(-1), _BitsPerChannel(-1), _ColourMode(-1) {
+}
+
+psdHeaderInfo::~psdHeaderInfo() {
+}
+	
+bool psdHeaderInfo::Read(FreeImageIO *io, fi_handle handle) {
+	psdHeader header;
+
+	const int n = (int)io->read_proc(&header, sizeof(header), 1, handle);
+	if(!n) {
+		return false;
+	}
+
+	// check the signature
+	int nSignature = psdGetValue(header.Signature, sizeof(header.Signature));
+	if (PSD_SIGNATURE == nSignature) {
+		// check the version
+		int nVersion = psdGetValue( header.Version, sizeof(header.Version) );
+		if (1 == nVersion) {
+			// header.Reserved must be zero
+			BYTE psd_reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+			if(memcmp(header.Reserved, psd_reserved, 6) != 0) {
+				FreeImage_OutputMessageProc(FIF_PSD, "Warning: file header reserved member is not equal to zero");
+			}
+			// read the header
+			_Channels = (short)psdGetValue( header.Channels, sizeof(header.Channels) );
+			_Height = psdGetValue( header.Rows, sizeof(header.Rows) );
+			_Width = psdGetValue( header.Columns, sizeof(header.Columns) );
+			_BitsPerChannel = (short)psdGetValue( header.Depth, sizeof(header.Depth) );
+			_ColourMode = (short)psdGetValue( header.Mode, sizeof(header.Mode) );
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+// --------------------------------------------------------------------------
+
+psdColourModeData::psdColourModeData() : _Length(-1), _plColourData(NULL) {
+}
+
+psdColourModeData::~psdColourModeData() { 
+	SAFE_DELETE_ARRAY(_plColourData); 
+}
+	
+bool psdColourModeData::Read(FreeImageIO *io, fi_handle handle) {
+	if (0 < _Length) { 
+		SAFE_DELETE_ARRAY(_plColourData);
+	}
+	
+	BYTE Length[4];
+	io->read_proc(&Length, sizeof(Length), 1, handle);
+	
+	_Length = psdGetValue( Length, sizeof(_Length) );
+	if (0 < _Length) {
+		_plColourData = new BYTE[_Length];
+		io->read_proc(_plColourData, _Length, 1, handle);
+	}
+
+	return true;
+}
+	
+bool psdColourModeData::FillPalette(FIBITMAP *dib) {
+	RGBQUAD *pal = FreeImage_GetPalette(dib);
+	if(pal) {
+		for (int i = 0; i < 256; i++) {
+			pal[i].rgbRed	= _plColourData[i + 0*256];
+			pal[i].rgbGreen = _plColourData[i + 1*256];
+			pal[i].rgbBlue	= _plColourData[i + 2*256];
+		}
+		return true;
+	}
+	return false;
+}
+
+// --------------------------------------------------------------------------
+
+psdImageResource::psdImageResource() : _plName (0) { 
+	Reset(); 
+}
+
+psdImageResource::~psdImageResource() { 
+	SAFE_DELETE_ARRAY(_plName);
+}
+
+void psdImageResource::Reset() {
+	_Length = -1;
+	memset( _OSType, '\0', sizeof(_OSType) );
+	_ID = -1;
+	SAFE_DELETE_ARRAY(_plName);
+	_Size = -1;
+}
+
+// --------------------------------------------------------------------------
+
+psdResolutionInfo::psdResolutionInfo() : _widthUnit(-1), _heightUnit(-1), _hRes(-1), _vRes(-1), _hResUnit(-1), _vResUnit(-1) {
+}
+
+psdResolutionInfo::~psdResolutionInfo() {
+}
+	
+int psdResolutionInfo::Read(FreeImageIO *io, fi_handle handle) {
+	BYTE IntValue[4], ShortValue[2];
+	int nBytes=0, n;
+	
+	// Horizontal resolution in pixels per inch.
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_hRes = (short)psdGetValue(ShortValue, sizeof(_hRes) );
+	// 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm.
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_hResUnit = psdGetValue(IntValue, sizeof(_hResUnit) );
+	// Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_widthUnit = (short)psdGetValue(ShortValue, sizeof(_widthUnit) );
+	// Vertical resolution in pixels per inch.
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_vRes = (short)psdGetValue(ShortValue, sizeof(_vRes) );
+	// 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm.
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_vResUnit = psdGetValue(IntValue, sizeof(_vResUnit) );
+	// Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_heightUnit = (short)psdGetValue(ShortValue, sizeof(_heightUnit) );
+	
+	return nBytes;
+}
+
+void psdResolutionInfo::GetResolutionInfo(unsigned &res_x, unsigned &res_y) {
+	if(_hResUnit == 1) {
+		// convert pixels / inch to pixel / m
+		res_x = (unsigned) (_hRes / 0.0254000 + 0.5);
+	} else if(_hResUnit == 2) {
+		// convert pixels / cm to pixel / m
+		res_x = (unsigned) (_hRes * 100.0 + 0.5);
+	}
+	if(_vResUnit == 1) {
+		// convert pixels / inch to pixel / m
+		res_y = (unsigned) (_vRes / 0.0254000 + 0.5);
+	} else if(_vResUnit == 2) {
+		// convert pixels / cm to pixel / m
+		res_y = (unsigned) (_vRes * 100.0 + 0.5);
+	}
+}
+
+// --------------------------------------------------------------------------
+
+psdResolutionInfo_v2::psdResolutionInfo_v2() { 
+	_Channels = _Rows = _Columns = _Depth = _Mode = -1; 
+}
+
+psdResolutionInfo_v2::~psdResolutionInfo_v2() {
+}
+
+int psdResolutionInfo_v2::Read(FreeImageIO *io, fi_handle handle) {
+	BYTE ShortValue[2];
+	int nBytes=0, n;
+	
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Channels = (short)psdGetValue(ShortValue, sizeof(_Channels) );
+
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Rows = (short)psdGetValue(ShortValue, sizeof(_Rows) );
+	
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Columns = (short)psdGetValue(ShortValue, sizeof(_Columns) );
+
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Depth = (short)psdGetValue(ShortValue, sizeof(_Depth) );
+
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Mode = (short)psdGetValue(ShortValue, sizeof(_Mode) );
+	
+	return nBytes;
+}
+
+// --------------------------------------------------------------------------
+
+psdDisplayInfo::psdDisplayInfo() {
+	_Opacity = _ColourSpace = -1;
+	for (unsigned n = 0; n < 4; ++n) {
+		_Colour[n] = 0;
+	}
+	_Kind = 0;
+	_padding = '0';
+}
+
+psdDisplayInfo::~psdDisplayInfo() {
+}
+	
+int psdDisplayInfo::Read(FreeImageIO *io, fi_handle handle) {
+	BYTE ShortValue[2];
+	int nBytes=0, n;
+	
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_ColourSpace = (short)psdGetValue(ShortValue, sizeof(_ColourSpace) );
+	
+	for (unsigned i = 0; i < 4; ++i) {
+		n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+		nBytes += n * sizeof(ShortValue);
+		_Colour[i] = (short)psdGetValue(ShortValue, sizeof(_Colour[i]) );
+	}
+	
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Opacity = (short)psdGetValue(ShortValue, sizeof(_Opacity) );
+	if((_Opacity < 0) || (_Opacity > 100)) {
+		throw "Invalid DisplayInfo::Opacity value";
+	}
+	
+	BYTE c[1];
+	n = (int)io->read_proc(&c, sizeof(c), 1, handle);
+	nBytes += n * sizeof(c);
+	_Kind = (BYTE)psdGetValue(c, sizeof(c));
+	
+	n = (int)io->read_proc(&c, sizeof(c), 1, handle);
+	nBytes += n * sizeof(c);
+	_padding = (BYTE)psdGetValue(c, sizeof(c));
+	if(_padding != 0) {
+		throw "Invalid DisplayInfo::Padding value";
+	}
+	
+	return nBytes;
+}
+
+// --------------------------------------------------------------------------
+
+psdThumbnail::psdThumbnail() : 
+_Format(-1), _Width(-1), _Height(-1), _WidthBytes(-1), _Size(-1), _CompressedSize(-1), _BitPerPixel(-1), _Planes(-1), _dib(NULL) {
+}
+
+psdThumbnail::~psdThumbnail() { 
+	FreeImage_Unload(_dib);
+}
+
+int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) {
+	BYTE ShortValue[2], IntValue[4];
+	int nBytes=0, n;
+
+	// remove the header size (28 bytes) from the total data size
+	int iTotalData = iResourceSize - 28;
+
+	const long block_end = io->tell_proc(handle) + iTotalData;	
+	
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_Format = psdGetValue(IntValue, sizeof(_Format) );
+	
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_Width = psdGetValue(IntValue, sizeof(_Width) );
+	
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_Height = psdGetValue(IntValue, sizeof(_Height) );
+	
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) );
+
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_Size = psdGetValue(IntValue, sizeof(_Size) );
+
+	n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+	nBytes += n * sizeof(IntValue);
+	_CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) );
+
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) );
+
+	n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+	nBytes += n * sizeof(ShortValue);
+	_Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) );
+
+	const long JFIF_startpos = io->tell_proc(handle);
+
+	if(_dib) {
+		FreeImage_Unload(_dib);
+	}
+
+	if(_Format == 1) {
+		// kJpegRGB thumbnail image
+		_dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle);
+		if(isBGR) {
+			SwapRedBlue32(_dib);
+		}			
+		// HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available! 
+		io->seek_proc(handle, block_end, SEEK_SET);
+	}
+	else {
+		// kRawRGB thumbnail image		
+		// ### Unimplemented (should be trivial)
+
+		// skip the thumbnail part
+		io->seek_proc(handle, iTotalData, SEEK_CUR);
+		return iResourceSize;
+	}
+	
+	nBytes += (block_end - JFIF_startpos); 
+
+	return nBytes;
+}
+
+//---------------------------------------------------------------------------
+
+psdICCProfile::psdICCProfile() : _ProfileSize(0), _ProfileData(NULL) {
+}
+
+psdICCProfile::~psdICCProfile() {
+	clear();
+}
+
+void psdICCProfile::clear() { SAFE_DELETE_ARRAY(_ProfileData); _ProfileSize = 0;}
+
+int psdICCProfile::Read(FreeImageIO *io, fi_handle handle, int size) {
+	int nBytes = 0, n;
+	
+	clear();
+	
+	_ProfileData = new (std::nothrow) BYTE[size];
+	if(NULL != _ProfileData) {
+		n = (int)io->read_proc(_ProfileData, 1, size, handle);
+		_ProfileSize = size;
+		nBytes += n * sizeof(BYTE);
+	}
+
+	return nBytes;
+}
+
+//---------------------------------------------------------------------------
+
+/**
+Invert only color components, skipping Alpha/Black
+(Can be useful as public/utility function)
+*/
+static
+BOOL invertColor(FIBITMAP* dib) {
+	FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib);
+	const unsigned Bpp = FreeImage_GetBPP(dib)/8;
+	
+	if((type == FIT_BITMAP && Bpp == 4) || type == FIT_RGBA16) {
+		const unsigned width = FreeImage_GetWidth(dib);
+		const unsigned height = FreeImage_GetHeight(dib);
+		BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+		const unsigned pitch = FreeImage_GetPitch(dib);
+		const unsigned triBpp = Bpp - (Bpp == 4 ? 1 : 2);
+				
+		for(unsigned y = 0; y < height; y++) {
+			BYTE *line = line_start;
+
+			for(unsigned x = 0; x < width; x++) {
+				for(unsigned b=0; b < triBpp; ++b) {
+					line[b] = ~line[b];
+				}
+					
+				line += Bpp;
+			}
+			line_start += pitch;
+		}
+		
+		return TRUE;
+	}
+	else {
+		return FreeImage_Invert(dib);
+	}
+}
+
+//---------------------------------------------------------------------------
+
+psdParser::psdParser() {
+	_bThumbnailFilled = false;
+	_bDisplayInfoFilled = false;
+	_bResolutionInfoFilled = false;
+	_bResolutionInfoFilled_v2 = false;
+	_bCopyright = false;
+	_GlobalAngle = 30;
+	_ColourCount = -1;
+	_TransparentIndex = -1;
+	_fi_flags = 0;
+	_fi_format_id = FIF_UNKNOWN;
+}
+
+psdParser::~psdParser() {
+}
+
+bool psdParser::ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle)	{
+	bool bSuccess = false;
+	
+	BYTE DataLength[4];
+	int nBytes = 0;
+	int n = (int)io->read_proc(&DataLength, sizeof(DataLength), 1, handle);
+	int nTotalBytes = psdGetValue( DataLength, sizeof(DataLength) );
+	
+	BYTE data[1];
+	while( n && ( nBytes < nTotalBytes ) ) {
+		data[0] = '\0';
+		n = (int)io->read_proc(&data, sizeof(data), 1, handle);
+		nBytes += n * sizeof(data);
+	}
+	
+	if ( nBytes == nTotalBytes ) {
+		bSuccess = true;
+	}
+	
+	return bSuccess;
+}
+
+bool psdParser::ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length) {
+	psdImageResource oResource;
+	bool bSuccess = false;
+	
+	if(length > 0) {
+		oResource._Length = length;
+	} else {
+		BYTE Length[4];
+		int n = (int)io->read_proc(&Length, sizeof(Length), 1, handle);
+		
+		oResource._Length = psdGetValue( Length, sizeof(oResource._Length) );
+	}
+	
+	int nBytes = 0;
+	int nTotalBytes = oResource._Length;
+	
+	while(nBytes < nTotalBytes) {
+		int n = 0;
+		oResource.Reset();
+		
+		n = (int)io->read_proc(&oResource._OSType, sizeof(oResource._OSType), 1, handle);
+		if(n != 1) {
+			FreeImage_OutputMessageProc(_fi_format_id, "This file contains damaged data causing an unexpected end-of-file - stop reading resources");
+			return false;
+		}
+		nBytes += n * sizeof(oResource._OSType);
+
+		if( (nBytes % 2) != 0 ) {
+			return false;
+		}
+		
+		int nOSType = psdGetValue((BYTE*)&oResource._OSType, sizeof(oResource._OSType));
+
+		if ( PSD_RESOURCE == nOSType ) {
+			BYTE ID[2];
+			n = (int)io->read_proc(&ID, sizeof(ID), 1, handle);
+			nBytes += n * sizeof(ID);
+			
+			oResource._ID = (short)psdGetValue( ID, sizeof(ID) );
+			
+			BYTE SizeOfName;
+			n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle);
+			nBytes += n * sizeof(SizeOfName);
+			
+			int nSizeOfName = psdGetValue( &SizeOfName, sizeof(SizeOfName) );
+			if ( 0 < nSizeOfName ) {
+				oResource._plName = new BYTE[nSizeOfName];
+				n = (int)io->read_proc(oResource._plName, nSizeOfName, 1, handle);
+				nBytes += n * nSizeOfName;
+			}
+			
+			if ( 0 == (nSizeOfName % 2) ) {
+				n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle);
+				nBytes += n * sizeof(SizeOfName);
+			}
+			
+			BYTE Size[4];
+			n = (int)io->read_proc(&Size, sizeof(Size), 1, handle);
+			nBytes += n * sizeof(Size);
+			
+			oResource._Size = psdGetValue( Size, sizeof(oResource._Size) );
+			
+			if ( 0 != (oResource._Size % 2) ) {
+				// resource data must be even
+				oResource._Size++;
+			}
+			if ( 0 < oResource._Size ) {
+				BYTE IntValue[4];
+				BYTE ShortValue[2];
+				
+				switch( oResource._ID ) {
+					case 1000:
+						// Obsolete - Photoshop 2.0
+						_bResolutionInfoFilled_v2 = true;
+						nBytes += _resolutionInfo_v2.Read(io, handle);
+						break;
+					
+					// ResolutionInfo structure
+					case 1005:
+						_bResolutionInfoFilled = true;
+						nBytes += _resolutionInfo.Read(io, handle);
+						break;
+						
+					// DisplayInfo structure
+					case 1007:
+						_bDisplayInfoFilled = true;
+						nBytes += _displayInfo.Read(io, handle);
+						break;
+						
+					// (Photoshop 4.0) Copyright flag
+					// Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...
+					case 1034:
+						n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+						nBytes += n * sizeof(ShortValue);
+						_bCopyright = (1 == psdGetValue(ShortValue, sizeof(ShortValue)));
+						break;
+						
+					// (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only
+					case 1033:
+					// (Photoshop 5.0) Thumbnail resource (supersedes resource 1033)
+					case 1036:
+					{
+						_bThumbnailFilled = true;
+						bool bBGR = (1033==oResource._ID);
+						nBytes += _thumbnail.Read(io, handle, oResource._Size, bBGR);
+						break;
+					}
+					
+					// (Photoshop 5.0) Global Angle
+					// 4 bytes that contain an integer between 0 and 359, which is the global
+					// lighting angle for effects layer. If not present, assumed to be 30.
+					case 1037:
+						n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+						nBytes += n * sizeof(IntValue);
+						_GlobalAngle = psdGetValue(IntValue, sizeof(_GlobalAngle) );
+						break;
+
+					// ICC profile
+					case 1039:
+						nBytes += _iccProfile.Read(io, handle, oResource._Size);
+						break;
+
+					// (Photoshop 6.0) Indexed Color Table Count
+					// 2 bytes for the number of colors in table that are actually defined
+					case 1046:
+						n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+						nBytes += n * sizeof(ShortValue);
+						_ColourCount = (short)psdGetValue(ShortValue, sizeof(ShortValue) );
+						break;
+						
+					// (Photoshop 6.0) Transparency Index.
+					// 2 bytes for the index of transparent color, if any.
+					case 1047:
+						n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+						nBytes += n * sizeof(ShortValue);
+						_TransparentIndex = (short)psdGetValue(ShortValue, sizeof(ShortValue) );
+						break;
+						
+					default:
+					{
+						// skip resource
+						unsigned skip_length = MIN(oResource._Size, nTotalBytes - nBytes);
+						io->seek_proc(handle, skip_length, SEEK_CUR);
+						nBytes += skip_length;
+					}
+					break;
+				}
+			}
+		}
+  }
+  
+  if (nBytes == nTotalBytes) {
+	  bSuccess = true;
+  }
+  
+  return bSuccess;
+  
+} 
+
+FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) {
+	if(handle == NULL) 
+		return NULL;
+	
+	bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+	
+	WORD nCompression = 0;
+	io->read_proc(&nCompression, sizeof(nCompression), 1, handle);
+	
+#ifndef FREEIMAGE_BIGENDIAN
+	SwapShort(&nCompression);
+#endif
+	
+	if((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE))	{
+		FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression);
+		return NULL;
+	}
+	
+	const unsigned nWidth = _headerInfo._Width;
+	const unsigned nHeight = _headerInfo._Height;
+	const unsigned nChannels = _headerInfo._Channels;
+	const unsigned depth = _headerInfo._BitsPerChannel;
+	const unsigned bytes = (depth == 1) ? 1 : depth / 8;
+		
+	// channel(plane) line (BYTE aligned)
+	const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes;
+	
+	if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) {
+		FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth);
+		return NULL;
+	}
+	
+	// build output buffer
+	
+	FIBITMAP* bitmap = NULL;
+	unsigned dstCh = 0;
+	
+	short mode = _headerInfo._ColourMode;
+	
+	if(mode == PSDP_MULTICHANNEL && nChannels < 3) {
+		// CM 
+		mode = PSDP_GRAYSCALE; // C as gray, M as extra channel
+	}
+		
+	bool needPalette = false;
+	switch (mode) {
+		case PSDP_BITMAP:
+		case PSDP_DUOTONE:	
+		case PSDP_INDEXED:
+		case PSDP_GRAYSCALE:
+			dstCh = 1;
+			switch(depth) {
+				case 16:
+				bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh);
+				break;
+				case 32:
+				bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh);
+				break;
+				default: // 1-, 8-
+				needPalette = true;
+				bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
+				break;
+			}
+			break;
+		case PSDP_RGB:	
+		case PSDP_LAB:		
+		case PSDP_CMYK	:
+		case PSDP_MULTICHANNEL	:
+			// force PSDP_MULTICHANNEL CMY as CMYK
+			dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4);
+			if(dstCh < 3) {
+				throw "Invalid number of channels";
+			}
+
+			switch(depth) {
+				case 16:
+				bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh);
+				break;
+				case 32:
+				bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh);
+				break;
+				default:
+				bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
+				break;
+			}
+			break;
+		default:
+			throw "Unsupported color mode";
+			break;
+	}
+	if(!bitmap) {
+		throw FI_MSG_ERROR_DIB_MEMORY;
+	}
+
+	// write thumbnail
+	FreeImage_SetThumbnail(bitmap, _thumbnail.getDib());
+		
+	// @todo Add some metadata model
+		
+	if(header_only) {
+		return bitmap;
+	}
+	
+	// Load pixels data
+
+	const unsigned dstChannels = dstCh;
+	
+	const unsigned dstBpp =  (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8;
+	const unsigned dstLineSize = FreeImage_GetPitch(bitmap);	
+	BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped
+	
+	BYTE* line_start = new BYTE[lineSize]; //< fileline cache
+
+	switch ( nCompression ) {
+		case PSDP_COMPRESSION_NONE: // raw data	
+		{			
+			for(unsigned c = 0; c < nChannels; c++) {
+				if(c >= dstChannels) {
+					// @todo write extra channels
+					break; 
+				}
+					
+				const unsigned channelOffset = c * bytes;
+				
+				BYTE* dst_line_start = dst_first_line;
+				for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped
+
+					io->read_proc(line_start, lineSize, 1, handle);
+					
+					for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; 
+						line += bytes, dst_line += dstBpp) {
+#ifdef FREEIMAGE_BIGENDIAN
+							memcpy(dst_line + channelOffset, line, bytes);
+#else
+						// reverse copy bytes
+						for (unsigned b = 0; b < bytes; ++b) {
+							dst_line[channelOffset + b] = line[(bytes-1) - b];
+						}
+#endif // FREEIMAGE_BIGENDIAN
+					}
+				} //< h
+			}//< ch
+			
+			SAFE_DELETE_ARRAY(line_start);
+					
+		}
+		break;
+		
+		case PSDP_COMPRESSION_RLE: // RLE compression	
+		{			
+									
+			// The RLE-compressed data is preceeded by a 2-byte line size for each row in the data,
+			// store an array of these
+
+			// later use this array as WORD rleLineSizeList[nChannels][nHeight];
+			WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight];
+
+			if(!rleLineSizeList) {
+				FreeImage_Unload(bitmap);
+				SAFE_DELETE_ARRAY(line_start);
+				throw std::bad_alloc();
+			}	
+			
+			io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle);
+			
+			WORD largestRLELine = 0;
+			for(unsigned ch = 0; ch < nChannels; ++ch) {
+				for(unsigned h = 0; h < nHeight; ++h) {
+					const unsigned index = ch * nHeight + h;
+
+#ifndef FREEIMAGE_BIGENDIAN 
+					SwapShort(&rleLineSizeList[index]);
+#endif
+					if(largestRLELine < rleLineSizeList[index]) {
+						largestRLELine = rleLineSizeList[index];
+					}
+				}
+			}
+
+			BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine];
+			if(!rle_line_start) {
+				FreeImage_Unload(bitmap);
+				SAFE_DELETE_ARRAY(line_start);
+				SAFE_DELETE_ARRAY(rleLineSizeList);
+				throw std::bad_alloc();
+			}
+			
+			// Read the RLE data (assume 8-bit)
+			
+			const BYTE* const line_end = line_start + lineSize;
+
+			for (unsigned ch = 0; ch < nChannels; ch++) {
+				const unsigned channelOffset = ch * bytes;
+				
+				BYTE* dst_line_start = dst_first_line;
+				for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped
+					const unsigned index = ch * nHeight + h;
+					
+					// - read and uncompress line -
+					
+					const WORD rleLineSize = rleLineSizeList[index];
+					
+					io->read_proc(rle_line_start, rleLineSize, 1, handle);
+					
+					for (BYTE* rle_line = rle_line_start, *line = line_start; 
+						rle_line < rle_line_start + rleLineSize, line < line_end;) {
+
+						int len = *rle_line++;
+						
+						// NOTE len is signed byte in PackBits RLE
+						
+						if ( len < 128 ) { //<- MSB is not set
+							// uncompressed packet
+							
+							// (len + 1) bytes of data are copied
+							
+							++len;
+							
+							// assert we don't write beyound eol
+							memcpy(line, rle_line, line + len > line_end ? line_end - line : len);
+							line += len;
+							rle_line += len;
+						}
+						else if ( len > 128 ) { //< MSB is set
+						
+							// RLE compressed packet
+							
+							// One byte of data is repeated (–len + 1) times
+							
+							len ^= 0xFF; // same as (-len + 1) & 0xFF 
+							len += 2;    //
+
+							// assert we don't write beyound eol
+							memset(line, *rle_line++, line + len > line_end ? line_end - line : len);							
+							line += len;
+
+						}
+						else if ( 128 == len ) {
+							// Do nothing
+						}
+					}//< rle_line
+					
+					// - write line to destination -
+					
+					if(ch >= dstChannels) {
+						// @todo write to extra channels
+						break; 
+					}
+						
+					// byte by byte copy a single channel to pixel
+					for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; 
+						line += bytes, dst_line += dstBpp) {
+
+#ifdef FREEIMAGE_BIGENDIAN
+							memcpy(dst_line + channelOffset, line, bytes);
+#else
+							// reverse copy bytes
+							for (unsigned b = 0; b < bytes; ++b) {
+								dst_line[channelOffset + b] = line[(bytes-1) - b];							
+							}
+#endif // FREEIMAGE_BIGENDIAN
+					}	
+				}//< h
+			}//< ch
+			
+			SAFE_DELETE_ARRAY(line_start);
+			SAFE_DELETE_ARRAY(rleLineSizeList);
+			SAFE_DELETE_ARRAY(rle_line_start);
+		}
+		break;
+		
+		case 2: // ZIP without prediction, no specification
+			break;
+			
+		case 3: // ZIP with prediction, no specification
+			break;
+			
+		default: // Unknown format
+			break;
+		
+	}
+	
+	// --- Further process the bitmap ---
+	
+	if((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) {	
+		// CMYK values are "inverted", invert them back		
+
+		if(mode == PSDP_MULTICHANNEL) {
+			invertColor(bitmap);
+		} else {
+			FreeImage_Invert(bitmap);
+		}
+
+		if((_fi_flags & PSD_CMYK) == PSD_CMYK) {
+			// keep as CMYK
+
+			if(mode == PSDP_MULTICHANNEL) {
+				//### we force CMY to be CMYK, but CMY has no ICC. 
+				// Create empty profile and add the flag.
+				FreeImage_CreateICCProfile(bitmap, NULL, 0);
+				FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK;
+			}
+		}
+		else { 
+			// convert to RGB
+			
+			ConvertCMYKtoRGBA(bitmap);
+			
+			// The ICC Profile is no longer valid
+			_iccProfile.clear();
+			
+			// remove the pending A if not present in source 
+			if(nChannels == 4 || nChannels == 3 ) {
+				FIBITMAP* t = RemoveAlphaChannel(bitmap);
+				if(t) {
+					FreeImage_Unload(bitmap);
+					bitmap = t;
+				} // else: silently fail
+			}
+		}
+	}
+	else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) {
+		ConvertLABtoRGB(bitmap);
+	}
+	else {
+		if (needPalette && FreeImage_GetPalette(bitmap)) {
+			
+			if(mode == PSDP_BITMAP) {
+				CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2);
+			}
+			else if(mode == PSDP_INDEXED) {
+				if(!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) {
+					FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one.");
+				} else {
+					_colourModeData.FillPalette(bitmap);
+				}
+			}
+			// GRAYSCALE, DUOTONE - use default grayscale palette
+		}
+		
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+		if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) {
+			SwapRedBlue32(bitmap);
+		}
+#endif
+	}
+	
+	return bitmap;
+} 
+
+FIBITMAP* psdParser::Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags) {
+	FIBITMAP *Bitmap = NULL;
+	
+	_fi_flags = flags;
+	_fi_format_id = s_format_id;
+	
+	try {
+		if (NULL == handle) {
+			throw("Cannot open file");
+		}
+		
+		if (!_headerInfo.Read(io, handle)) {
+			throw("Error in header");
+		}
+
+		if (!_colourModeData.Read(io, handle)) {
+			throw("Error in ColourMode Data");
+		}
+		
+		if (!ReadImageResources(io, handle)) {
+			throw("Error in Image Resource");
+		}
+		
+		if (!ReadLayerAndMaskInfoSection(io, handle)) {
+			throw("Error in Mask Info");
+		}
+		
+		Bitmap = ReadImageData(io, handle);
+		if (NULL == Bitmap) {
+			throw("Error in Image Data");
+		}
+
+		// set resolution info
+		if(NULL != Bitmap) {
+			unsigned res_x = 2835;	// 72 dpi
+			unsigned res_y = 2835;	// 72 dpi
+			if (_bResolutionInfoFilled) {
+				_resolutionInfo.GetResolutionInfo(res_x, res_y);
+			}
+			FreeImage_SetDotsPerMeterX(Bitmap, res_x);
+			FreeImage_SetDotsPerMeterY(Bitmap, res_y);	
+		}
+
+		// set ICC profile
+		FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize);
+		if ((flags & PSD_CMYK) == PSD_CMYK) {
+			short mode = _headerInfo._ColourMode;
+			if((mode == PSDP_CMYK) || (mode == PSDP_MULTICHANNEL)) {
+				FreeImage_GetICCProfile(Bitmap)->flags |= FIICC_COLOR_IS_CMYK;
+			}
+		}
+		
+	} catch(const char *text) {
+		FreeImage_OutputMessageProc(s_format_id, text);
+	}
+	catch(const std::exception& e) {
+		FreeImage_OutputMessageProc(s_format_id, "%s", e.what());
+	}
+
+	return Bitmap;
+} 
diff --git a/files/Source/FreeImage/PSDParser.h b/files/Source/FreeImage/PSDParser.h
new file mode 100644
index 0000000..15ab542
--- /dev/null
+++ b/files/Source/FreeImage/PSDParser.h
@@ -0,0 +1,271 @@
+// ==========================================================
+// Photoshop Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef PSDPARSER_H
+#define PSDPARSER_H
+
+/**
+Table 2-12: File header section. 
+The file header contains the basic properties of the image. 
+*/
+typedef struct psdHeader {
+	BYTE Signature[4];	//! Always equal 8BPS, do not try to read the file if the signature does not match this value.
+	BYTE Version[2];	//! Always equal 1, do not read file if the version does not match this value.
+	char Reserved[6];	//! Must be zero.
+	BYTE Channels[2];	//! Number of channels including any alpha channels, supported range is 1 to 24.
+	BYTE Rows[4];		//! The height of the image in pixels. Supported range is 1 to 30,000.
+	BYTE Columns[4];	//! The width of the image in pixels. Supported range is 1 to 30,000.
+	BYTE Depth[2];		//! The number of bits per channel. Supported values are 1, 8, and 16.
+	BYTE Mode[2];		//! Colour mode of the file, Bitmap=0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, Multichannel=7, Duotone=8, Lab=9. 
+} psdHeader;
+
+/**
+Table 2-12: HeaderInfo Color spaces
+@see psdHeader
+*/
+class psdHeaderInfo {
+public:
+	short _Channels;	//! Numer of channels including any alpha channels, supported range is 1 to 24.
+	int   _Height;		//! The height of the image in pixels. Supported range is 1 to 30,000.
+	int   _Width;		//! The width of the image in pixels. Supported range is 1 to 30,000.
+	short _BitsPerChannel;//! The number of bits per channel. Supported values are 1, 8, and 16.
+	short _ColourMode;	//! Colour mode of the file, Bitmap=0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, Multichannel=7, Duotone=8, Lab=9. 
+
+public:
+	psdHeaderInfo();
+	~psdHeaderInfo();
+	/**
+	@return Returns the number of bytes read
+	*/
+	bool Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table 2-13 Color mode data section
+
+Only indexed color and duotone have color mode data. For all other modes,
+this section is just 4 bytes: the length field, which is set to zero.
+For indexed color images, the length will be equal to 768, and the color data
+will contain the color table for the image, in non-interleaved order.
+For duotone images, the color data will contain the duotone specification,
+the format of which is not documented. Other applications that read
+Photoshop files can treat a duotone image as a grayscale image, and just
+preserve the contents of the duotone information when reading and writing
+the file.
+*/
+class psdColourModeData {
+public:
+	int _Length;			//! The length of the following color data
+	BYTE * _plColourData;	//! The color data
+
+public:
+	psdColourModeData();
+	~psdColourModeData();
+	/**
+	@return Returns the number of bytes read
+	*/
+	bool Read(FreeImageIO *io, fi_handle handle);
+	bool FillPalette(FIBITMAP *dib);
+};
+
+/**
+Table 2-1: Image resource block
+NB: Resource data is padded to make size even
+*/
+class psdImageResource {
+public:
+	int     _Length;
+	char    _OSType[4];	//! Photoshop always uses its signature, 8BIM
+	short   _ID;		//! Unique identifier. Image resource IDs on page 8
+	BYTE * _plName;		//! A pascal string, padded to make size even (a null name consists of two bytes of 0)
+	int     _Size;		//! Actual size of resource data. This does not include the Type, ID, Name or Size fields.
+
+public:
+	psdImageResource();
+	~psdImageResource();
+	void Reset();
+};
+
+/**
+Table A-6: ResolutionInfo structure
+This structure contains information about the resolution of an image. It is
+written as an image resource. See the Document file formats chapter for more
+details.
+*/
+class psdResolutionInfo {
+public:
+	short _widthUnit;	//! Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+	short _heightUnit;	//! Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+	short _hRes;		//! Horizontal resolution in pixels per inch.
+	short _vRes;		//! Vertical resolution in pixels per inch.
+	int _hResUnit;		//! 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm.
+	int _vResUnit;		//! 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm.
+
+public:
+	psdResolutionInfo();
+	~psdResolutionInfo();	
+	/**
+	@return Returns the number of bytes read
+	*/
+	int Read(FreeImageIO *io, fi_handle handle);
+	/**
+	@param res_x [out] X resolution in pixels/meter
+	@param res_y [out] Y resolution in pixels/meter
+	*/
+	void GetResolutionInfo(unsigned &res_x, unsigned &res_y);
+};
+
+// Obsolete - Photoshop 2.0
+class psdResolutionInfo_v2 {
+public:
+	short _Channels;
+	short _Rows;
+	short _Columns;
+	short _Depth;
+	short _Mode;
+	
+public:
+	psdResolutionInfo_v2();
+	~psdResolutionInfo_v2();
+	/**
+	@return Returns the number of bytes read
+	*/
+	int Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table A-7: DisplayInfo Color spaces
+This structure contains display information about each channel. It is written as an image resource.
+*/
+class psdDisplayInfo {
+public:
+	short _ColourSpace;
+	short _Colour[4];
+	short _Opacity;  //! 0..100
+	BYTE _Kind;     //! selected = 0, protected = 1
+	BYTE _padding;  //! should be zero
+	
+public:
+	psdDisplayInfo();
+	~psdDisplayInfo();
+	/**
+	@return Returns the number of bytes read
+	*/
+	int Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table 2-5: Thumbnail resource header
+Adobe Photoshop 5.0 and later stores thumbnail information for preview
+display in an image resource block. These resource blocks consist of an initial
+28 byte header, followed by a JFIF thumbnail in RGB (red, green, blue) order
+for both Macintosh and Windows. Adobe Photoshop 4.0 stored the
+thumbnail information in the same format except the data section is BGR
+(blue, green, red). The Adobe Photoshop 4.0 format is at resource ID 1033
+and the Adobe Photoshop 5.0 format is at resource ID 1036.
+*/
+class psdThumbnail {
+public:
+	int _Format;			//! = 1 (kJpegRGB). Also supports kRawRGB (0).
+	int _Width;				//! Width of thumbnail in pixels.
+	int _Height;			//! Height of thumbnail in pixels.
+	int _WidthBytes;		//! Padded row bytes as (width * bitspixel + 31) / 32 * 4.
+	int _Size;				//! Total size as widthbytes * height * planes
+	int _CompressedSize;	//! Size after compression. Used for consistentcy check. 
+	short _BitPerPixel;		//! = 24. Bits per pixel.
+	short _Planes;			//! = 1. Number of planes.
+	FIBITMAP * _dib;		//! JFIF data as uncompressed dib. Note: For resource ID 1033 the data is in BGR format.
+	
+public:
+	psdThumbnail();
+	~psdThumbnail();
+	FIBITMAP* getDib() { return _dib; }
+	/**
+	@return Returns the number of bytes read
+	*/
+	int Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR);
+
+private:
+	psdThumbnail(const psdThumbnail&);
+	psdThumbnail& operator=(const psdThumbnail&);
+};
+
+class psdICCProfile {
+public:
+	int _ProfileSize;
+	BYTE * _ProfileData;
+public:
+	psdICCProfile();
+	~psdICCProfile();
+	void clear();
+	/**
+	@return Returns the number of bytes read
+	*/
+	int Read(FreeImageIO *io, fi_handle handle, int size);
+};
+
+/**
+PSD loader
+*/
+class psdParser {
+private:
+	psdHeaderInfo			_headerInfo;
+	psdColourModeData		_colourModeData;
+	psdResolutionInfo		_resolutionInfo;
+	psdResolutionInfo_v2	_resolutionInfo_v2;
+	psdDisplayInfo			_displayInfo;
+	psdThumbnail			_thumbnail;
+	psdICCProfile			_iccProfile;
+
+	short _ColourCount;
+	short _TransparentIndex;
+	int _GlobalAngle;
+	bool _bResolutionInfoFilled;
+	bool _bResolutionInfoFilled_v2;
+	bool _bDisplayInfoFilled;
+	bool _bThumbnailFilled;
+	bool _bCopyright;
+
+	int _fi_flags;
+	int _fi_format_id;
+	
+private:
+	/**	Actually ignore it */
+	bool ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle);
+	FIBITMAP* ReadImageData(FreeImageIO *io, fi_handle handle);
+
+public:
+	psdParser();
+	~psdParser();
+	FIBITMAP* Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags=0);
+	/** Also used by the TIFF plugin */
+	bool ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length=0);
+	/** Used by the TIFF plugin */
+	FIBITMAP* GetThumbnail() {
+		return _thumbnail.getDib();
+	}
+};
+
+#endif // PSDPARSER_H
+
diff --git a/files/Source/FreeImage/PixelAccess.cpp b/files/Source/FreeImage/PixelAccess.cpp
new file mode 100644
index 0000000..b5714b2
--- /dev/null
+++ b/files/Source/FreeImage/PixelAccess.cpp
@@ -0,0 +1,197 @@
+// ==========================================================
+// Pixel access functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Ryan Rubley (ryan@lostreality.org)
+// - Riley McNiff (rmcniff@marexgroup.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+BYTE * DLL_CALLCONV
+FreeImage_GetScanLine(FIBITMAP *dib, int scanline) {
+	if(!FreeImage_HasPixels(dib)) {
+		return NULL;
+	}
+	return CalculateScanLine(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), scanline);
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) {
+	BYTE shift;
+
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+		return FALSE;
+
+	if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+		switch(FreeImage_GetBPP(dib)) {
+			case 1:
+				*value = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+				break;
+			case 4:
+				shift = (BYTE)((1 - x % 2) << 2);
+				*value = (bits[x >> 1] & (0x0F << shift)) >> shift;
+				break;
+			case 8:
+				*value = bits[x];
+				break;
+			default:
+				return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) {
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+		return FALSE;
+
+	if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+		switch(FreeImage_GetBPP(dib)) {
+			case 16:
+			{
+				bits += 2*x;
+				WORD *pixel = (WORD *)bits;
+				if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+					value->rgbBlue		= (BYTE)((((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+					value->rgbGreen		= (BYTE)((((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+					value->rgbRed		= (BYTE)((((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+					value->rgbReserved	= 0;
+				} else {
+					value->rgbBlue		= (BYTE)((((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+					value->rgbGreen		= (BYTE)((((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+					value->rgbRed		= (BYTE)((((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+					value->rgbReserved	= 0;
+				}
+				break;
+			}
+			case 24:
+				bits += 3*x;
+				value->rgbBlue		= bits[FI_RGBA_BLUE];	// B
+				value->rgbGreen		= bits[FI_RGBA_GREEN];	// G
+				value->rgbRed		= bits[FI_RGBA_RED];	// R
+				value->rgbReserved	= 0;
+				break;
+			case 32:
+				bits += 4*x;
+				value->rgbBlue		= bits[FI_RGBA_BLUE];	// B
+				value->rgbGreen		= bits[FI_RGBA_GREEN];	// G
+				value->rgbRed		= bits[FI_RGBA_RED];	// R
+				value->rgbReserved	= bits[FI_RGBA_ALPHA];	// A
+				break;
+			default:
+				return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) {
+	BYTE shift;
+
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+		return FALSE;
+
+	if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+		switch(FreeImage_GetBPP(dib)) {
+			case 1:
+				*value ? bits[x >> 3] |= (0x80 >> (x & 0x7)) : bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+				break;
+			case 4:
+				shift = (BYTE)((1 - x % 2) << 2);
+				bits[x >> 1] &= ~(0x0F << shift);
+				bits[x >> 1] |= ((*value & 0x0F) << shift);
+				break;
+			case 8:
+				bits[x] = *value;
+				break;
+			default:
+				return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) {
+	if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+		return FALSE;
+
+	if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+		BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+		switch(FreeImage_GetBPP(dib)) {
+			case 16:
+			{
+				bits += 2*x;
+				WORD *pixel = (WORD *)bits;
+				if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+					*pixel = ((value->rgbBlue >> 3) << FI16_565_BLUE_SHIFT) |
+						((value->rgbGreen >> 2) << FI16_565_GREEN_SHIFT) |
+						((value->rgbRed >> 3) << FI16_565_RED_SHIFT);
+				} else {
+					*pixel = ((value->rgbBlue >> 3) << FI16_555_BLUE_SHIFT) |
+						((value->rgbGreen >> 3) << FI16_555_GREEN_SHIFT) |
+						((value->rgbRed >> 3) << FI16_555_RED_SHIFT);
+				}
+				break;
+			}
+			case 24:
+				bits += 3*x;
+				bits[FI_RGBA_BLUE]	= value->rgbBlue;	// B
+				bits[FI_RGBA_GREEN] = value->rgbGreen;	// G
+				bits[FI_RGBA_RED]	= value->rgbRed;	// R
+				break;
+			case 32:
+				bits += 4*x;
+				bits[FI_RGBA_BLUE]	= value->rgbBlue;		// B
+				bits[FI_RGBA_GREEN] = value->rgbGreen;		// G
+				bits[FI_RGBA_RED]	= value->rgbRed;		// R
+				bits[FI_RGBA_ALPHA] = value->rgbReserved;	// A
+				break;
+			default:
+				return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
diff --git a/files/Source/FreeImage/Plugin.cpp b/files/Source/FreeImage/Plugin.cpp
new file mode 100644
index 0000000..6e2d072
--- /dev/null
+++ b/files/Source/FreeImage/Plugin.cpp
@@ -0,0 +1,818 @@
+// =====================================================================
+// FreeImage Plugin Interface
+//
+// Design and implementation by
+// - Floris van den Berg (floris@geekhq.nl)
+// - Rui Lopes (ruiglopes@yahoo.com)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Petr Pytelka (pyta@lightcomp.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// =====================================================================
+
+#ifdef _MSC_VER 
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#else
+#include <ctype.h>
+#endif // _WIN32
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+// =====================================================================
+
+using namespace std;
+
+// =====================================================================
+// Plugin search list
+// =====================================================================
+
+const char *
+s_search_list[] = {
+	"",
+	"plugins\\",
+};
+
+static int s_search_list_size = sizeof(s_search_list) / sizeof(char *);
+static PluginList *s_plugins = NULL;
+static int s_plugin_reference_count = 0;
+
+
+// =====================================================================
+// Reimplementation of stricmp (it is not supported on some systems)
+// =====================================================================
+
+int
+FreeImage_stricmp(const char *s1, const char *s2) {
+	int c1, c2;
+
+	do {
+		c1 = tolower(*s1++);
+		c2 = tolower(*s2++);
+	} while (c1 && c1 == c2);
+
+	return c1 - c2;
+}
+
+// =====================================================================
+//  Implementation of PluginList
+// =====================================================================
+
+PluginList::PluginList() :
+m_plugin_map(),
+m_node_count(0) {
+}
+
+FREE_IMAGE_FORMAT
+PluginList::AddNode(FI_InitProc init_proc, void *instance, const char *format, const char *description, const char *extension, const char *regexpr) {
+	if (init_proc != NULL) {
+		PluginNode *node = new(std::nothrow) PluginNode;
+		Plugin *plugin = new(std::nothrow) Plugin;
+		if(!node || !plugin) {
+			if(node) delete node;
+			if(plugin) delete plugin;
+			FreeImage_OutputMessageProc(FIF_UNKNOWN, FI_MSG_ERROR_MEMORY);
+			return FIF_UNKNOWN;
+		}
+
+		memset(plugin, 0, sizeof(Plugin));
+
+		// fill-in the plugin structure
+		// note we have memset to 0, so all unset pointers should be NULL)
+
+		init_proc(plugin, (int)m_plugin_map.size());
+
+		// get the format string (two possible ways)
+
+		const char *the_format = NULL;
+
+		if (format != NULL) {
+			the_format = format;
+		} else if (plugin->format_proc != NULL) {
+			the_format = plugin->format_proc();
+		}
+
+		// add the node if it wasn't there already
+
+		if (the_format != NULL) {
+			node->m_id = (int)m_plugin_map.size();
+			node->m_instance = instance;
+			node->m_plugin = plugin;
+			node->m_format = format;
+			node->m_description = description;
+			node->m_extension = extension;
+			node->m_regexpr = regexpr;
+			node->m_enabled = TRUE;
+
+			m_plugin_map[(const int)m_plugin_map.size()] = node;
+
+			return (FREE_IMAGE_FORMAT)node->m_id;
+		}
+
+		// something went wrong while allocating the plugin... cleanup
+
+		delete plugin;
+		delete node;
+	}
+
+	return FIF_UNKNOWN;
+}
+
+PluginNode *
+PluginList::FindNodeFromFormat(const char *format) {
+	for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+		const char *the_format = ((*i).second->m_format != NULL) ? (*i).second->m_format : (*i).second->m_plugin->format_proc();
+
+		if ((*i).second->m_enabled) {
+			if (FreeImage_stricmp(the_format, format) == 0) {
+				return (*i).second;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+PluginNode *
+PluginList::FindNodeFromMime(const char *mime) {
+	for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+		const char *the_mime = ((*i).second->m_plugin->mime_proc != NULL) ? (*i).second->m_plugin->mime_proc() : "";
+
+		if ((*i).second->m_enabled) {
+			if ((the_mime != NULL) && (strcmp(the_mime, mime) == 0)) {
+				return (*i).second;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+PluginNode *
+PluginList::FindNodeFromFIF(int node_id) {
+	map<int, PluginNode *>::iterator i = m_plugin_map.find(node_id);
+
+	if (i != m_plugin_map.end()) {
+		return (*i).second;
+	}
+
+	return NULL;
+}
+
+int
+PluginList::Size() const {
+	return (int)m_plugin_map.size();
+}
+
+BOOL
+PluginList::IsEmpty() const {
+	return m_plugin_map.empty();
+}
+
+PluginList::~PluginList() {
+	for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+#ifdef _WIN32
+		if ((*i).second->m_instance != NULL) {
+			FreeLibrary((HINSTANCE)(*i).second->m_instance);
+		}
+#endif
+		delete (*i).second->m_plugin;
+		delete ((*i).second);
+	}
+}
+
+// =====================================================================
+// Retrieve a pointer to the plugin list container
+// =====================================================================
+
+PluginList * DLL_CALLCONV
+FreeImage_GetPluginList() {
+	return s_plugins;
+}
+
+// =====================================================================
+// Plugin System Initialization
+// =====================================================================
+
+void DLL_CALLCONV
+FreeImage_Initialise(BOOL load_local_plugins_only) {
+	if (s_plugin_reference_count++ == 0) {
+		
+		/*
+		Note: initialize all singletons here 
+		in order to avoid race conditions with multi-threading
+		*/
+
+		// initialise the TagLib singleton
+		TagLib& s = TagLib::instance();
+
+		// internal plugin initialization
+
+		s_plugins = new(std::nothrow) PluginList;
+
+		if (s_plugins) {
+			/* NOTE : 
+			The order used to initialize internal plugins below MUST BE the same order 
+			as the one used to define the FREE_IMAGE_FORMAT enum. 
+			*/
+			s_plugins->AddNode(InitBMP);
+			s_plugins->AddNode(InitICO);
+			s_plugins->AddNode(InitJPEG);
+			s_plugins->AddNode(InitJNG);
+			s_plugins->AddNode(InitKOALA);
+			s_plugins->AddNode(InitIFF);
+			s_plugins->AddNode(InitMNG);
+			s_plugins->AddNode(InitPNM, NULL, "PBM", "Portable Bitmap (ASCII)", "pbm", "^P1");
+			s_plugins->AddNode(InitPNM, NULL, "PBMRAW", "Portable Bitmap (RAW)", "pbm", "^P4");
+			s_plugins->AddNode(InitPCD);
+			s_plugins->AddNode(InitPCX);
+			s_plugins->AddNode(InitPNM, NULL, "PGM", "Portable Greymap (ASCII)", "pgm", "^P2");
+			s_plugins->AddNode(InitPNM, NULL, "PGMRAW", "Portable Greymap (RAW)", "pgm", "^P5");
+			s_plugins->AddNode(InitPNG);
+			s_plugins->AddNode(InitPNM, NULL, "PPM", "Portable Pixelmap (ASCII)", "ppm", "^P3");
+			s_plugins->AddNode(InitPNM, NULL, "PPMRAW", "Portable Pixelmap (RAW)", "ppm", "^P6");
+			s_plugins->AddNode(InitRAS);
+			s_plugins->AddNode(InitTARGA);
+			s_plugins->AddNode(InitTIFF);
+			s_plugins->AddNode(InitWBMP);
+			s_plugins->AddNode(InitPSD);
+			s_plugins->AddNode(InitCUT);
+			s_plugins->AddNode(InitXBM);
+			s_plugins->AddNode(InitXPM);
+			s_plugins->AddNode(InitDDS);
+	        s_plugins->AddNode(InitGIF);
+	        s_plugins->AddNode(InitHDR);
+			s_plugins->AddNode(InitG3);
+			s_plugins->AddNode(InitSGI);
+			s_plugins->AddNode(InitEXR);
+			s_plugins->AddNode(InitJ2K);
+			s_plugins->AddNode(InitJP2);
+			s_plugins->AddNode(InitPFM);
+			s_plugins->AddNode(InitPICT);
+          s_plugins->AddNode(InitWEBP);
+			
+			// external plugin initialization
+
+#ifdef _WIN32
+			if (!load_local_plugins_only) {
+				int count = 0;
+				char buffer[MAX_PATH + 200];
+				wchar_t current_dir[2 * _MAX_PATH], module[2 * _MAX_PATH];
+				BOOL bOk = FALSE;
+
+				// store the current directory. then set the directory to the application location
+
+				if (GetCurrentDirectoryW(2 * _MAX_PATH, current_dir) != 0) {
+					if (GetModuleFileNameW(NULL, module, 2 * _MAX_PATH) != 0) {
+						wchar_t *last_point = wcsrchr(module, L'\\');
+
+						if (last_point) {
+							*last_point = L'\0';
+
+							bOk = SetCurrentDirectoryW(module);
+						}
+					}
+				}
+
+				// search for plugins
+
+				while (count < s_search_list_size) {
+					_finddata_t find_data;
+					long find_handle;
+
+					strcpy(buffer, s_search_list[count]);
+					strcat(buffer, "*.fip");
+
+					if ((find_handle = (long)_findfirst(buffer, &find_data)) != -1L) {
+						do {
+							strcpy(buffer, s_search_list[count]);
+							strncat(buffer, find_data.name, MAX_PATH + 200);
+
+							HINSTANCE instance = LoadLibrary(buffer);
+
+							if (instance != NULL) {
+								FARPROC proc_address = GetProcAddress(instance, "_Init@8");
+
+								if (proc_address != NULL) {
+									s_plugins->AddNode((FI_InitProc)proc_address, (void *)instance);
+								} else {
+									FreeLibrary(instance);
+								}
+							}
+						} while (_findnext(find_handle, &find_data) != -1L);
+
+						_findclose(find_handle);
+					}
+
+					count++;
+				}
+
+				// restore the current directory
+
+				if (bOk) {
+					SetCurrentDirectoryW(current_dir);
+				}
+			}
+#endif // _WIN32
+		}
+	}
+}
+
+void DLL_CALLCONV
+FreeImage_DeInitialise() {
+	--s_plugin_reference_count;
+
+	if (s_plugin_reference_count == 0) {
+		delete s_plugins;
+	}
+}
+
+// =====================================================================
+// Open and close a bitmap
+// =====================================================================
+
+void * DLL_CALLCONV
+FreeImage_Open(PluginNode *node, FreeImageIO *io, fi_handle handle, BOOL open_for_reading) {
+	if (node->m_plugin->open_proc != NULL) {
+       return node->m_plugin->open_proc(io, handle, open_for_reading);
+	}
+
+	return NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_Close(PluginNode *node, FreeImageIO *io, fi_handle handle, void *data) {
+	if (node->m_plugin->close_proc != NULL) {
+		node->m_plugin->close_proc(io, handle, data);
+	}
+}
+
+// =====================================================================
+// Plugin System Load/Save Functions
+// =====================================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
+	if ((fif >= 0) && (fif < FreeImage_GetFIFCount())) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+		
+		if (node != NULL) {
+			if(node->m_plugin->load_proc != NULL) {
+				void *data = FreeImage_Open(node, io, handle, TRUE);
+					
+				FIBITMAP *bitmap = node->m_plugin->load_proc(io, handle, -1, flags, data);
+					
+				FreeImage_Close(node, io, handle, data);
+					
+				return bitmap;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Load(FREE_IMAGE_FORMAT fif, const char *filename, int flags) {
+	FreeImageIO io;
+	SetDefaultIO(&io);
+	
+	FILE *handle = fopen(filename, "rb");
+
+	if (handle) {
+		FIBITMAP *bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)handle, flags);
+
+		fclose(handle);
+
+		return bitmap;
+	} else {
+		FreeImage_OutputMessageProc((int)fif, "FreeImage_Load: failed to open file %s", filename);
+	}
+
+	return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags) {
+	FreeImageIO io;
+	SetDefaultIO(&io);
+#ifdef _WIN32	
+	FILE *handle = _wfopen(filename, L"rb");
+
+	if (handle) {
+		FIBITMAP *bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)handle, flags);
+
+		fclose(handle);
+
+		return bitmap;
+	} else {
+		FreeImage_OutputMessageProc((int)fif, "FreeImage_LoadU: failed to open input file");
+	}
+#endif
+	return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveToHandle(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags) {
+	// cannot save "header only" formats
+	if(FreeImage_HasPixels(dib) == FALSE) {
+		FreeImage_OutputMessageProc((int)fif, "FreeImage_SaveToHandle: cannot save \"header only\" formats");
+		return FALSE;
+	}
+
+	if ((fif >= 0) && (fif < FreeImage_GetFIFCount())) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+		
+		if (node) {
+			if(node->m_plugin->save_proc != NULL) {
+				void *data = FreeImage_Open(node, io, handle, FALSE);
+					
+				BOOL result = node->m_plugin->save_proc(io, dib, handle, -1, flags, data);
+					
+				FreeImage_Close(node, io, handle, data);
+					
+				return result;
+			}
+		}
+	}
+
+	return FALSE;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_Save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags) {
+	FreeImageIO io;
+	SetDefaultIO(&io);
+	
+	FILE *handle = fopen(filename, "w+b");
+	
+	if (handle) {
+		BOOL success = FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)handle, flags);
+
+		fclose(handle);
+
+		return success;
+	} else {
+		FreeImage_OutputMessageProc((int)fif, "FreeImage_Save: failed to open file %s", filename);
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveU(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags) {
+	FreeImageIO io;
+	SetDefaultIO(&io);
+#ifdef _WIN32	
+	FILE *handle = _wfopen(filename, L"w+b");
+	
+	if (handle) {
+		BOOL success = FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)handle, flags);
+
+		fclose(handle);
+
+		return success;
+	} else {
+		FreeImage_OutputMessageProc((int)fif, "FreeImage_SaveU: failed to open output file");
+	}
+#endif
+	return FALSE;
+}
+
+// =====================================================================
+// Plugin construction + enable/disable functions
+// =====================================================================
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_RegisterLocalPlugin(FI_InitProc proc_address, const char *format, const char *description, const char *extension, const char *regexpr) {
+	return s_plugins->AddNode(proc_address, NULL, format, description, extension, regexpr);
+}
+
+#ifdef _WIN32
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_RegisterExternalPlugin(const char *path, const char *format, const char *description, const char *extension, const char *regexpr) {
+	if (path != NULL) {
+		HINSTANCE instance = LoadLibrary(path);
+
+		if (instance != NULL) {
+			FARPROC proc_address = GetProcAddress(instance, "_Init@8");
+
+			FREE_IMAGE_FORMAT result = s_plugins->AddNode((FI_InitProc)proc_address, (void *)instance, format, description, extension, regexpr);
+
+			if (result == FIF_UNKNOWN)
+				FreeLibrary(instance);
+
+			return result;
+		}
+	}
+
+	return FIF_UNKNOWN;
+}
+#endif // _WIN32
+
+int DLL_CALLCONV
+FreeImage_SetPluginEnabled(FREE_IMAGE_FORMAT fif, BOOL enable) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		if (node != NULL) {
+			BOOL previous_state = node->m_enabled;
+
+			node->m_enabled = enable;
+
+			return previous_state;
+		}
+	}
+
+	return -1;
+}
+
+int DLL_CALLCONV
+FreeImage_IsPluginEnabled(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? node->m_enabled : FALSE;
+	}
+	
+	return -1;
+}
+
+// =====================================================================
+// Plugin Access Functions
+// =====================================================================
+
+int DLL_CALLCONV
+FreeImage_GetFIFCount() {
+	return (s_plugins != NULL) ? s_plugins->Size() : 0;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromFormat(const char *format) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFormat(format);
+
+		return (node != NULL) ? (FREE_IMAGE_FORMAT)node->m_id : FIF_UNKNOWN;
+	}
+
+	return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromMime(const char *mime) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromMime(mime);
+
+		return (node != NULL) ? (FREE_IMAGE_FORMAT)node->m_id : FIF_UNKNOWN;
+	}
+
+	return FIF_UNKNOWN;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFormatFromFIF(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? (node->m_format != NULL) ? node->m_format : node->m_plugin->format_proc() : NULL;
+	}
+
+	return NULL;
+}
+
+const char * DLL_CALLCONV 
+FreeImage_GetFIFMimeType(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? (node->m_plugin != NULL) ? ( node->m_plugin->mime_proc != NULL )? node->m_plugin->mime_proc() : NULL : NULL : NULL;
+	}
+
+	return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFExtensionList(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? (node->m_extension != NULL) ? node->m_extension : (node->m_plugin->extension_proc != NULL) ? node->m_plugin->extension_proc() : NULL : NULL;
+	}
+
+	return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFDescription(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? (node->m_description != NULL) ? node->m_description : (node->m_plugin->description_proc != NULL) ? node->m_plugin->description_proc() : NULL : NULL;
+	}
+
+	return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFRegExpr(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? (node->m_regexpr != NULL) ? node->m_regexpr : (node->m_plugin->regexpr_proc != NULL) ? node->m_plugin->regexpr_proc() : NULL : NULL;
+	}
+
+	return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsReading(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? node->m_plugin->load_proc != NULL : FALSE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsWriting(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? node->m_plugin->save_proc != NULL : FALSE ;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsExportBPP(FREE_IMAGE_FORMAT fif, int depth) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? 
+			(node->m_plugin->supports_export_bpp_proc != NULL) ? 
+				node->m_plugin->supports_export_bpp_proc(depth) : FALSE : FALSE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsExportType(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? 
+			(node->m_plugin->supports_export_type_proc != NULL) ? 
+				node->m_plugin->supports_export_type_proc(type) : FALSE : FALSE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsICCProfiles(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? 
+			(node->m_plugin->supports_icc_profiles_proc != NULL) ? 
+				node->m_plugin->supports_icc_profiles_proc() : FALSE : FALSE;
+	}
+
+	return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsNoPixels(FREE_IMAGE_FORMAT fif) {
+	if (s_plugins != NULL) {
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		return (node != NULL) ? 
+			(node->m_plugin->supports_no_pixels_proc != NULL) ? 
+				node->m_plugin->supports_no_pixels_proc() : FALSE : FALSE;
+	}
+
+	return FALSE;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromFilename(const char *filename) {
+	if (filename != NULL) {
+		const char *extension;
+
+		// get the proper extension if we received a filename
+
+		char *place = strrchr((char *)filename, '.');	
+		extension = (place != NULL) ? ++place : filename;
+
+		// look for the extension in the plugin table
+
+		for (int i = 0; i < FreeImage_GetFIFCount(); ++i) {
+
+			if (s_plugins->FindNodeFromFIF(i)->m_enabled) {
+
+				// compare the format id with the extension
+
+				if (FreeImage_stricmp(FreeImage_GetFormatFromFIF((FREE_IMAGE_FORMAT)i), extension) == 0) {
+					return (FREE_IMAGE_FORMAT)i;
+				} else {
+					// make a copy of the extension list and split it
+
+					char *copy = (char *)malloc(strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)) + 1);
+					memset(copy, 0, strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)) + 1);
+					memcpy(copy, FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i), strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)));
+
+					// get the first token
+
+					char *token = strtok(copy, ",");
+
+					while (token != NULL) {
+						if (FreeImage_stricmp(token, extension) == 0) {
+							free(copy);
+
+								return (FREE_IMAGE_FORMAT)i;
+						}
+
+						token = strtok(NULL, ",");
+					}
+
+					// free the copy of the extension list
+
+					free(copy);
+				}	
+			}
+		}
+	}
+
+	return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV 
+FreeImage_GetFIFFromFilenameU(const wchar_t *filename) {
+#ifdef _WIN32	
+	if (filename == NULL) return FIF_UNKNOWN;
+    	
+	// get the proper extension if we received a filename
+	wchar_t *place = wcsrchr((wchar_t *)filename, '.');	
+	if (place == NULL) return FIF_UNKNOWN;
+	// convert to single character - no national chars in extensions
+	char *extension = (char *)malloc(wcslen(place)+1);
+	unsigned int i=0;
+	for(; i < wcslen(place); i++) // convert 16-bit to 8-bit
+		extension[i] = (char)(place[i] & 0x00FF);
+	// set terminating 0
+	extension[i]=0;
+	FREE_IMAGE_FORMAT fRet = FreeImage_GetFIFFromFilename(extension);
+	free(extension);
+
+	return fRet;
+#else
+	return FIF_UNKNOWN;
+#endif // _WIN32
+}
+
+BOOL DLL_CALLCONV
+FreeImage_Validate(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle) {
+	if (s_plugins != NULL) {
+		BOOL validated = FALSE;
+
+		PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+		if (node) {
+			long tell = io->tell_proc(handle);
+
+			validated = (node != NULL) ? (node->m_enabled) ? (node->m_plugin->validate_proc != NULL) ? node->m_plugin->validate_proc(io, handle) : FALSE : FALSE : FALSE;
+
+			io->seek_proc(handle, tell, SEEK_SET);
+		}
+
+		return validated;
+	}
+
+	return FALSE;
+}
diff --git a/files/Source/FreeImage/PluginBMP.cpp b/files/Source/FreeImage/PluginBMP.cpp
new file mode 100644
index 0000000..0ee1c5d
--- /dev/null
+++ b/files/Source/FreeImage/PluginBMP.cpp
@@ -0,0 +1,1494 @@
+// ==========================================================
+// BMP Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Michal Novotny (michal@etc.cz)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+//   Constants + headers
+// ----------------------------------------------------------
+
+static const BYTE RLE_COMMAND     = 0;
+static const BYTE RLE_ENDOFLINE   = 0;
+static const BYTE RLE_ENDOFBITMAP = 1;
+static const BYTE RLE_DELTA       = 2;
+
+static const BYTE BI_RGB            = 0;	// compression: none
+static const BYTE BI_RLE8           = 1;	// compression: RLE 8-bit/pixel
+static const BYTE BI_RLE4           = 2;	// compression: RLE 4-bit/pixel
+static const BYTE BI_BITFIELDS      = 3;	// compression: Bit field or Huffman 1D compression for BITMAPCOREHEADER2
+static const BYTE BI_JPEG           = 4;	// compression: JPEG or RLE-24 compression for BITMAPCOREHEADER2
+static const BYTE BI_PNG            = 5;	// compression: PNG
+static const BYTE BI_ALPHABITFIELDS = 6;	// compression: Bit field (this value is valid in Windows CE .NET 4.0 and later)
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagBITMAPCOREHEADER {
+  DWORD   bcSize;
+  WORD    bcWidth;
+  WORD    bcHeight;
+  WORD    bcPlanes;
+  WORD    bcBitCnt;
+} BITMAPCOREHEADER, *PBITMAPCOREHEADER; 
+
+typedef struct tagBITMAPINFOOS2_1X_HEADER {
+  DWORD  biSize;
+  WORD   biWidth;
+  WORD   biHeight; 
+  WORD   biPlanes; 
+  WORD   biBitCount;
+} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER; 
+
+typedef struct tagBITMAPFILEHEADER {
+  WORD    bfType;		//! The file type
+  DWORD   bfSize;		//! The size, in bytes, of the bitmap file
+  WORD    bfReserved1;	//! Reserved; must be zero
+  WORD    bfReserved2;	//! Reserved; must be zero
+  DWORD   bfOffBits;	//! The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits
+} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapInfoHeader(BITMAPINFOHEADER *header) {
+	SwapLong(&header->biSize);
+	SwapLong((DWORD *)&header->biWidth);
+	SwapLong((DWORD *)&header->biHeight);
+	SwapShort(&header->biPlanes);
+	SwapShort(&header->biBitCount);
+	SwapLong(&header->biCompression);
+	SwapLong(&header->biSizeImage);
+	SwapLong((DWORD *)&header->biXPelsPerMeter);
+	SwapLong((DWORD *)&header->biYPelsPerMeter);
+	SwapLong(&header->biClrUsed);
+	SwapLong(&header->biClrImportant);
+}
+
+static void
+SwapCoreHeader(BITMAPCOREHEADER *header) {
+	SwapLong(&header->bcSize);
+	SwapShort(&header->bcWidth);
+	SwapShort(&header->bcHeight);
+	SwapShort(&header->bcPlanes);
+	SwapShort(&header->bcBitCnt);
+}
+
+static void
+SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header) {
+	SwapLong(&header->biSize);
+	SwapShort(&header->biWidth);
+	SwapShort(&header->biHeight);
+	SwapShort(&header->biPlanes);
+	SwapShort(&header->biBitCount);
+}
+
+static void
+SwapFileHeader(BITMAPFILEHEADER *header) {
+	SwapShort(&header->bfType);
+  	SwapLong(&header->bfSize);
+  	SwapShort(&header->bfReserved1);
+  	SwapShort(&header->bfReserved2);
+	SwapLong(&header->bfOffBits);
+}
+#endif
+
+// --------------------------------------------------------------------------
+
+/**
+Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param dib Image to be loaded 
+@param height Image height
+@param pitch Image pitch
+@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, unsigned pitch, unsigned bit_count) {
+	unsigned count = 0;
+
+	// Load pixel data
+	// NB: height can be < 0 for BMP data
+	if (height > 0) {
+		count = io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
+		if(count != 1) {
+			return FALSE;
+		}
+	} else {
+		int positiveHeight = abs(height);
+		for (int c = 0; c < positiveHeight; ++c) {
+			count = io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, handle);
+			if(count != 1) {
+				return FALSE;
+			}
+		}
+	}
+
+	// swap as needed
+#ifdef FREEIMAGE_BIGENDIAN
+	if (bit_count == 16) {
+		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+			WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+				SwapShort(pixel);
+				pixel++;
+			}
+		}
+	}
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+	if (bit_count == 24 || bit_count == 32) {
+		for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+			BYTE *pixel = FreeImage_GetScanLine(dib, y);
+			for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+				INPLACESWAP(pixel[0], pixel[2]);
+				pixel += (bit_count >> 3);
+			}
+		}
+	}
+#endif
+
+	return TRUE;
+}
+
+/**
+Load image pixels for 4-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded 
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL 
+LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
+	int status_byte = 0;
+	BYTE second_byte = 0;
+	int bits = 0;
+
+	BYTE *pixels = NULL;	// temporary 8-bit buffer
+
+	try {
+		height = abs(height);
+
+		pixels = (BYTE*)malloc(width * height * sizeof(BYTE));
+		if(!pixels) throw(1);
+		memset(pixels, 0, width * height * sizeof(BYTE));
+
+		BYTE *q = pixels;
+		BYTE *end = pixels + height * width;
+
+		for (int scanline = 0; scanline < height; ) {
+			if (q < pixels || q  >= end) {
+				break;
+			}
+			if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+				throw(1);
+			}
+			if (status_byte != 0)	{
+				status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+				// Encoded mode
+				if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+					throw(1);
+				}
+				for (int i = 0; i < status_byte; i++)	{
+					*q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+				}
+				bits += status_byte;
+			}
+			else {
+				// Escape mode
+				if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+					throw(1);
+				}
+				switch (status_byte) {
+					case RLE_ENDOFLINE:
+					{
+						// End of line
+						bits = 0;
+						scanline++;
+						q = pixels + scanline*width;
+					}
+					break;
+
+					case RLE_ENDOFBITMAP:
+						// End of bitmap
+						q = end;
+						break;
+
+					case RLE_DELTA:
+					{
+						// read the delta values
+
+						BYTE delta_x = 0;
+						BYTE delta_y = 0;
+
+						if(io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+							throw(1);
+						}
+						if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+							throw(1);
+						}
+
+						// apply them
+
+						bits += delta_x;
+						scanline += delta_y;
+						q = pixels + scanline*width+bits;
+					}
+					break;
+
+					default:
+					{
+						// Absolute mode
+						status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+						for (int i = 0; i < status_byte; i++) {
+							if ((i & 0x01) == 0) {
+								if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+									throw(1);
+								}
+							}
+							*q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+						}